Jump to the navigation

Controls & Interaction

Status: This is not ready for detailed review. It is an in-progress, unapproved editor’s draft.

«««< HEAD After the basic styling was applied to the content, the interaction comes in. In the first steps the slide show is prepared to hide all content but the first slide. Then “left” and “right” arrows are added to the carousel and enabled, so users can skip through the individual slides.

Add Styling When Javascript Is Enabled

When JavaScript is enabled, the carousel wrapper is getting a fixed height. Individual slides are stacked on top of another, with the current slide on top of the others. Additionally, all slides but the current one are hidden by using display: none – a hiding technique that hides the inactive slides visually as well as from assistive technologies.

To allow basic navigating through the slides, the slide show needs to be prepared to hide all content but the first slide from all users, including people using assistive technologies. Then “left” and “right” arrows are added to the carousel, that display the previous and next slide, respectively.

Add Styling when JavaScript is Enabled

If JavaScript is enabled a class (.active) is added that allows all slides to be stacked on top of another with the current slide top-most. Additionally, all slides but the current one are hidden by using display: none – a hiding technique that hides visually and from assistive technologies. »»»> origin/dev

Code snippet: CSS
<<<<<<< HEAD
.carousel.active {
  height: 480px;
  overflow:hidden;
  border: 1px solid #333;
  position:relative;
}
=======
>>>>>>> origin/dev

.active .slide {
  border: none;
  display: none;
  position:absolute;
  top:0;
  left:0;
}

.slide.current {
  display:block;
  z-index: 500;
}

Additionally, we add a active class to the carousel wrapper and a current class to the first slide:

Code snippet: JavaScript
var carousel = document.getElementById('c1');
carousel.querySelectorAll('.slide')[0].className = 'current slide';
carousel.className = 'active carousel';

The outcome looks like this:

Example:

Featured Articles:

Previous and Next Buttons

First, the previous and next buttons need to be added to the markup using JavaScript and styled according to their function. The HTML5 button element is used to make sure that the buttons are keyboard accessible.

Code snippet: JavaScript
var ctrls = document.createElement('ul');

ctrls.className = 'controls';
ctrls.innerHTML = '<li>' +
    '<button type="button" class="btn-prev">' +
      '<img src="../../img/chevron-left-75c7dd0b.png" alt="Previous Slide">' +
    '</button>' +
  '</li>' +
  '<li>' +
    '<button type="button" class="btn-next">' +
      '<img src="../../img/chevron-right-2f19bc8b.png" alt="Next Slide">' +
    '</button>' +
  '</li>';

  ctrls.querySelector('.prev').addEventListener('click', function(){
    prevSlide();
  });

  ctrls.querySelector('.next').addEventListener('click', function(){
    nextSlide();
  });

carousel.appendChild(ctrls);

The styling is very similar to the text of the slides. Contrast ratio requirements are valid here as well as on normal text. Both buttons get wider if they are hovered with the mouse or focused with the keyboard. This is also animated to keep the click target large for a longer time, which makes it easier for people with shaky hands to click them.

Code snippet: CSS
  .btn-prev,
  .btn-next {
    position:absolute;
    z-index: 700;
    top: 50%;
    margin-top: -2.5em;
    border:0;
    background: rgba(255,255,255,.6);
    line-height: 1;
    padding:2em .5em;
    transition: padding .4s ease-out;
  }

  .btn-next:hover, .btn-next:focus,
  .btn-prev:hover, .btn-prev:focus {
    padding-left: 2em;
    padding-right:2em;
  }

  .btn-prev {
    left:0;
    border-radius: 0 .25em .25em 0;
  }

  .btn-next {
    right:0;
    border-radius: .25em 0 0 .25em;
  }

And this is how the outcome looks and works:

Example:

Featured Articles:

List of Slides

Optionally, a list of slides can be used to provide the user a sense of how much information is in a slide and which the current slide is in the sequence of slides.

Example:

Numbers in squares (described below)

Visually, the above controls show three numbered square buttons. The current slide button (number 1) has rounded corners and is in the reverse color combination to the others, button 3 has a dashed border to show that it is the control currently in tab focus (or hovered by a mouse pointer). If number 3 was activated, slide 3 would be shown.

To convey the same quality of information to users who can’t see the screen, the controls need to be marked up as a list, so screen readers can then inform users of the number of items in the list. Also the current slide needs to be identified, for example by visually hidden text. Accurate labeling of the buttons will let the user know which slide is shown.

The script should be capable of identifying the current slide and rendering its identification within the text or text alternative for the current button.

Note: Numbered buttons are important for speech recognition software users as they cannot know what command to give their software to navigate to the desired slide otherwise.

The list is created through JavaScript and then added to the DOM of the carousel:

Code snippet: JavaScript
var slidenav = document.createElement('ul');

slidenav.className = 'slidenav';

forEachElement(slides, function(el, i){
  var li = document.createElement('li');
  var klass = (i===0) ? 'class="current" ' : '';
  var currentText = (i===0) ? ' <span class="visuallyhidden">(Current Slide)</span>' : '';

  li.innerHTML = '<button ' + klass + 'data-slide="' + i + '"><span class="visuallyhidden">News</span> ' + (i+1) + currentText + '</button>';
  slidenav.appendChild(li);
});

carousel.className = 'active carousel with-slidenav';
carousel.appendChild(slidenav);
Code snippet: DOM representation of the JavaScript above
<ul class="slidenav">
  <li>
    <button class="current" data-slide="0">
      <span class="visuallyhidden">News</span> 1
      <span class="visuallyhidden">(Current Slide)</span>
    </button>
  </li>
  <li>
    <button data-slide="1">
      <span class="visuallyhidden">News</span> 2
    </button>
  </li>
  <li>
    <button data-slide="2">
      <span class="visuallyhidden">News</span> 3
    </button>
  </li>
</ul>

As shown above, the styling of the list is very important for accessibility as well:

Code snippet: CSS
.slidenav {
  position: absolute;
  bottom: 1em;
  left: 0;
  right: 0;
  text-align: center;
}

.slidenav li {
  display:inline-block;
  margin: 0 .5em;
}

.slidenav button {
  border: 2px solid #036;
  background-color: #036;
  line-height: 1em;
  height: 2em;
  width: 2em;
  font-weight: bold;
  color: #fff;
}

.slidenav button.current {
  border-radius: .5em;
  background-color: #fff;
  color: #333;
}

.slidenav button:hover,
.slidenav button:focus {
  border: 2px dashed #fff;
}

.slidenav button.current:hover,
.slidenav button.current:focus {
  border: 2px dashed #036;
}

When a user activates one of the buttons in the list, the slide should come up visually. Additionally, a focus should be set to the slide. This helps non-visual users as screen readers then read the slide immediately.

To do that, it is required to set the tabindex attribute to -1 as the slide <li> isn’t an element that can receive focus by default. The data-slide attribute is used to select the target slide.

Code snippet: JavaScript
function setSlides(new_current, setFocus = false) {

  new_current = parseFloat(new_current);

  var length = slides.length;
  var new_next = new_current+1;
  var new_prev = new_current-1;

  if(new_next === length) {
    new_next = 0;
  } else if(new_prev < 0) {
    new_prev = length-1;
  }

  for (var i = slides.length - 1; i >= 0; i--) {
    slides[i].className = "slide";
  };

  slides[new_next].className = 'next slide';
  slides[new_prev].className = 'prev slide';
  slides[new_current].className = 'current slide';

  if(with_slidenav) {
    var buttons = carousel.querySelectorAll('.slidenav button');
    for (var i = buttons.length - 1; i >= 0; i--) {
      buttons[i].className = "";
    };
    buttons[new_current].className = "current";
  }

  if (setFocus) {
    slides[new_current].setAttribute('tabindex', '-1');
    slides[new_current].focus();
  }

  index = new_current;
}

slidenav.addEventListener('click', function(event) {
  if (event.target.localName == 'button') {
    setSlides(event.target.getAttribute('data-slide'), true);
  }
}, true);

The final outcome then looks and works like this:

Example:

Featured Articles:

Success Criteria:

  • 1.3.1 Info and Relationships: Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. (Level A)

  • 2.1.1 Keyboard: All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user’s movement and not just the endpoints. (Level A)

  • 2.4.7 Focus Visible: Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible. (Level AA)

  • 4.1.2 Name, Role, Value: For all user interface components (including but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies. (Level A)