Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <h1>Carousel Experiment (Button Tags)</h1>
<p><label>Field before: <input type="text" name="before" id="before"></label></p>
<div id="carousel" aria-labelledby="cLabel" role="region">
    <h2 class="sr" aria-hidden="true" id="cLabel">News Items</h2>
    <ul>
        <li>
            <div class="img">
                <img src="http://www.test.assist.vt.edu/sandbox/assets/img/carousel/VT112188_2015181601.JPG"
                     alt="Researcher in lab">
            </div>
            <div class="blurb">
                <h3>
                    <a href="story1.html">Researchers closer to understanding how certain cancers
                resist treatment</a>
                </h3>
                <p>And here is some blurb text.</p>
            </div>
        </li>
        <li>
            <div class="img">
                <img
                    src="http://www.test.assist.vt.edu/sandbox/assets/img/carousel/VT109124_20150619023.JPG"
                    alt="Exercisers in spinning class look forward with determination">
            </div>
            <div class="blurb">
                <h3>
                    <a href="story2.html">How did Virginia Tech become the fittest campus in America?</a>
                </h3>
                <p>And here is some blurb text.</p>
            </div>
        </li>
        <li>
            <div class="img">
                <img
                    src="http://www.test.assist.vt.edu/sandbox/assets/img/carousel/1459353411656.jpg"
                    alt="Smiling student against backdrop of campus building">
            </div>
            <div class="blurb">
                <h3>
                    <a href="story3.html">Nneoma Nwankwo named Virginia Tech's Undergraduate Student of the Year</a>
                </h3>
                <p>And here is some blurb text.</p>
            </div>
        </li>
    </ul>
</div>
<p><label>Field after: <input type="text" name="after" id="after"></label></p>


<hr/>
<h2>Comments</h2>
<p>Inspired by <a href="https://www.w3.org/WAI/tutorials/carousels/">WAI example</a>, but modified.</p>

<p>This implementation of the carousel pattern uses button tags for the individual slide navigation controls.  The disadvantage of this is that its is not really semantically correct, in that this is really a group of buttons, only one of which can be selected, like a radio button group.  For implementation of the radio group pattern, see other pen.</p>

<p>Tested with JAWS 16/ IE 11, NVDA 2015.4/ Firefox 45.0.2, VoiceOver/Safari on OS X El Capitan (10.11.3)</p>

<h3>Problems:</h3>
<p>While the slideshow pauses if focus is on a focusable element in the carousel, such as the slide navigation controls, if one is using the virtual cursor to navigate through the page the content where the cursor is can change.</p>
<p>Hiding part of the button label may make it slightly harder for speech recognition users to activate controls.</p>
<h4>OS X/VoiceOver/Safari:</h4>
<ul><li>Occasionally, in response to a button press, the navigation keys stop working as expected, and, instead, move the visible cursor about the page</li></ul>
  <h4>iOS/VoiceOver/Safari:</h4>
<ul>
  <li>The first time you activate a liveControl, focus is shifted to the start of the page.  I've noticed this before on some other widgets.  Is this an iOS bug?  If so, how do I fix?</li>
  <li>The blurb text in the live region is not voiced, just the image and heading.  Why?</li>
  <li>When focus moves past carousel, it doesn't resume playing, though it does if I move before it.</li>
  <li>Occasionally, it just freaks out (to be technical).</li>
</ul>
<h4>JAWS 16/IE</h4>
<ul>
  <li>When the live region changes, only the heading text (and not the image alt text) is voiced and that is voiced twice.</li>
</ul>
<h4>NVDA/Firefox</h4>
<ul>
  <li>When you return to the Play/Pause button from a liveControl, it is voiced as being "SVG Play Icon (suspended)", even though shifting focus to it has caused it to change back to "SVG Pause Icon"</li>
</ul>

              
            
!

CSS

              
                .sr {
  position: absolute;
  clip: rect(1px 1px 1px 1px); /* for Internet Explorer */
  clip: rect(1px, 1px, 1px, 1px);
  padding: 0;
  border: 0;
  height: 1px;
  width: 1px;
  overflow: hidden;
}
#carousel {
  position: relative;
  max-height: 31.25rem;
  overflow: hidden;
  width:100%;
}
#playContainer {
  position: absolute;
  z-index: 1000;
  padding: .5rem;
}
.img {
  position: relative;
}
.img img {
  width:100%;
}
.blurb {
  position: absolute;
  bottom: 0;
  width: 100%;
  padding: .5rem;
  padding-bottom: 2rem;
  linear-gradient(transparent, black);
  background-image: linear-gradient(transparent, black);
  color: white;
  font-family: 'Lato', sans-serif;
}
.blurb h3 {
  font-size: 2em;
  text-shadow: 2px 2px 0px #000;
  margin-bottom: 0;
}
.blurb a {
  color: #fff;
  text-decoration: none;
}
.blurb a:hover, .blurb a:focus {
  text-decoration: underline;
}
#ctrls {
  position: absolute;
  bottom: 0;
  z-index: 1001;
  padding: .5rem;

}
#bullets, #prevNext {
  display: inline;
  padding: 0;
}
#bullets li, #prevNext li {
  display: inline;
}
#bullets button, #prevNext button {
  margin-right: .4em;
}
.activeBullet button {
  background-color: yellow;
}

/* Progressive enhancement: Hides carousel until ready */
.js #carousel {
  display:none;
}
              
            
!

JS

              
                $(document).ready(function(){

    makeCarousel();

    function makeCarousel() {
        var carousel = $('#carousel');  //Container for the slides and playback controls
        var numSlides = $('#carousel ul li').length;  //The number of slides in the list
        var currentSlide = 0;  //The index of the li in the ul for the slide currently displayed
        var playing = false;  //Whether slideshow is currently playing
        var intervalID;  //Reference to timer, so it can be removed to stop slideshow
        var fromCarousel = false;  //Whether focus had been on something in the carousel (except playPause)
        var slideList = $('#carousel ul').detach();  //Remove list from page, to pull slides from later


        //CREATE LIVE REGION AND CONTROLS AND ADD THEM TO PAGE
        //Create live region where slides will be displayed (aria-live state indicates whether to voice changes)
        var slideDisplay = $.parseHTML(
                '<div id="slideDisplay" ' +
                    'role="region" ' +
                    'aria-label="Currently Displayed Slide" ' +
                    'aria-live="off" ' +
                    'aria-relevant="additions" ' +
                    'aria-atomic="false">' +
                '</div>');

        //Create container for the controls that let you move between slides
        var ctrls = $.parseHTML('<nav id="ctrls" aria-label="Slide Navigation"></nav>');

        //Create list of bullet buttons for moving to particular slides
        var bullets = $.parseHTML('<ul id="bullets" aria-label="Individual Navigation"></ul>');
        for (var i = 1; i <= numSlides; i++) {
            $(bullets).append('' +
                    '<li>' +
                        '<button ' +
                            'class="liveControl" ' +
                            'aria-controls="slideDisplay">' +
                            '<span class="sr">Slide </span>'+i+
                        '</button>' +
                    '</li>');
        }

        //Create list containing previous and next buttons
        var prevNext =
                '<ul id="prevNext"  aria-label="Sequential Navigation">'+
                    '<li>' +
                        '<button ' +
                            'id="prev" ' +
                            'class="liveControl" ' +
                            'aria-label="Previous Slide" ' +
                            'aria-controls="slideDisplay">' +
                            'SVG Previous Icon' +
                        '</button>' +
                    '</li>' +
                    '<li>' +
                        '<button ' +
                            'id="next" ' +
                            'class="liveControl" ' +
                            'aria-label="Next Slide" ' +
                            'aria-controls="slideDisplay">' +
                            'SVG Next Icon' +
                        '</button>' +
                    '</li>' +
                '</ul>';

        //Create play/pause toggle button
        var playContainer =
                '<div id="playContainer">' +
                    '<button ' +
                        'id="playPause" ' +
                        'aria-label="Play" ' +
                        'aria-describedby="ctrlDesc" ' +
                        'aria-controls="slideDisplay">' +
                        'SVG Play Icon' +
                    '</button>' +
                    '<br /> '+
                    '<span ' +
                        'class="sr" ' +
                        'aria-hidden="true" ' +
                        'id="ctrlDesc">' +
                        'Plays/pauses slideshow. Slide display follows.' +
                    '</span>' +
                '</div>';

        //Add controls to carousel
        carousel.append(slideDisplay);
        $(ctrls).append(prevNext).append(bullets);
        carousel.append(ctrls);
        carousel.prepend(playContainer);


        //When user clicks on bullet button, show that slide and style the current button
        $("#bullets").find('button').click(function() {
            var item = $(this).closest('li');
            currentSlide = $(item).index();
            activate(currentSlide);
            event.stopPropagation();
        });

        //When user clicks previous button go to the previous slide
        $("#prev").click(function(event) {
            prevSlide();
            event.stopPropagation();
        });

        //When user clicks next button go to the next slide
        $("#next").click(function(event) {
            nextSlide();
            event.stopPropagation();
        });

        //When user clicks the play/pause toggle button, start or pause the slideshow
        $("#playPause").click(function(event) {
            togglePlay();
            event.stopPropagation();
        });


        /*
         If focus is on something that can cause a slide change, we want SR to voice changes to the slide display,
         but we don't want the slideshow to be automatically progressing, so we pause the slideshow
         */
        carousel.on('focusin', function(event) {
            //The exception is that, if focus is on playPause button, we don't want to suspend carousel,
            // because it might confuse the user to click on play and have it not do anything
            if ($(document.activeElement).attr('id')==='playPause') {
                if (fromCarousel) {
                    voiceChanges(false);
                    resume();
                }
                fromCarousel = false;
            } else {
                fromCarousel = true;
                suspend();
                voiceChanges(true);
                event.stopPropagation();
            }
        });

        /*
         If focus had been in the carousel, but then leaves it, either by tabbing out of it, or clicking elsewhere,
         then resume the slideshow and turn off live updates
         * */
        $(document).on('focusin', function(event) {
            if (fromCarousel) {
                voiceChanges(false);
                resume();
            }
            fromCarousel = false;
        }).on('click', function(event) {
            if (fromCarousel && !$(event.target).hasClass('liveControl')) {
                voiceChanges(false);
                resume();
            }
            fromCarousel = false;
        })

        //Activate the first slide
        activate(currentSlide);

        //Avoids flash of unstyled content
        $("html").removeClass("js");

        //Show slide and highlight related bullet button
        function activate(i) {
            showSlide(currentSlide);
            setActive(currentSlide);
        }

        //Take the contents of the selected list item and put in the slide display region
        function showSlide(i) {
            $(slideDisplay).html($(slideList).find('li:nth-child('+(i+1)+')').html());
        }

        //Show which slide is being displayed by highlighting related bullet button
        function setActive(i) {
            $('li.activeBullet').removeClass('activeBullet');
            currentItem = $('ul#bullets li:nth-child('+(i+1)+')');
            currentItem.addClass('activeBullet');

            $('span.activeIndicator').remove();
            currentItem.append('<span class="sr activeIndicator"> (Current Slide)</span>');
        }

        //Start or pause slideshow depending on whether it is playing currently
        function togglePlay() {
            if (playing) {
                $('#playPause').html('SVG Play Icon').attr('aria-label', 'play');
                pause();
                playing = false;
            } else {
                $('#playPause').html('SVG Pause Icon').attr('aria-label', 'pause');
                play();
                playing = true;
            }
        }

        //Start slideshow
        function play() {
            intervalID = setInterval(nextSlide, 5000);
        }

        //Pause slideshow
        function pause() {
            clearInterval(intervalID);
        }

        //Show previous slide
        function prevSlide() {
            currentSlide = (currentSlide === 0) ? numSlides-1 : currentSlide - 1;
            activate(currentSlide);
        }

        //Show next slide
        function nextSlide() {
            currentSlide = (currentSlide === numSlides-1) ? 0 : currentSlide + 1;
            activate(currentSlide);
        }

        //Make the ARIA live region politely announce changes
        function voiceChanges(toggle) {
            $(slideDisplay).attr('aria-live', toggle ? 'polite' : 'off');
        }

        //If slideshow is playing, put it in a suspended state, so it can resume playing when no longer on liveControl
        function suspend() {
            if (playing) {
                $('#playPause').html('SVG Play Icon (suspended)')
                        .attr('aria-label', 'Play (suspended)')
                        .addClass('suspended');
                pause();
            }
        }

        //If slideshow was playing when suspended, start it back up
        function resume() {
            if (playing) {
                $('#playPause').html('SVG Pause Icon').attr('aria-label', 'Pause').removeClass('suspended');
                play();
            }
        }
    }
});
              
            
!
999px

Console