npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@wanjapflueger/a11y-slider

v2.3.3

Published

A fully accessible slider

Downloads

14

Readme

a11y-slider

A slider carousel that is fully accessible. It has no design or theme applied. That way you can be certain the slider is usable and accessible and you can apply your own styles.

Pipeline status npm npm bundle size

a11y-slider screenshot

*Yes I know that the slider in the screenshot above has some example styles applied...


Table of contents


Requirements

  • You need to be able to import or require JavaScript files
  • You need to be able to import SCSS with SASS (since v2.0.0).

Browser support

  • Chrome 84
  • Firefox 63
  • Edge 84
  • Opera 73
  • Safari 14

Changelog

See changelog.md.

Install

npm i @wanjapflueger/a11y-slider

Example

example.wanjapflueger.de/a11y-slider/

Usage

Import class Slider:

import { Slider } from "@wanjapflueger/a11y-slider";
// var a11ySlider = require("@wanjapflueger/a11y-slider")

Create a new instance:

const mySlider = new Slider();
// const mySlider = new a11ySlider.Slider();

Methods

Method Create

  1. Create any reference element containing child elements. The contents of the each child element will be a single slide.

    <div id="my-list">
      <div>1</div>
      <div>2</div>
      ...
    </div>
  2. Assign that reference element with JavaScript to a variable, initiate a new instance of Slider and call method Create.

    import { Slider } from "@wanjapflueger/a11y-slider";
    
    const mySlider = new Slider();
    mySlider.Create({
      slides: document.getElementById('my-list')
    });

Parameters

See interface SliderParameters in src/index.ts.

slides

| Parameter | Required | Type | Default value | | --------- | -------- | ------------- | ------------- | | slides | ✅ Yes | HTMLElement | – |

Any HTMLElement containing child elements (HTMLUListElement in the example below). Of each child element (HTMLLIElement in the example below) the innerHTML will be placed inside a slide. This means the HTMLUListElement and HTMLLIElement[] will not be transferred to the slider, only the HTMLDivElement[] will, because they are inside the children of the HTMLUListElement.

Example:

<ul>
  <li><div class="item">A</div></li>
  <li><div class="item">B</div></li>
  <li><div class="item">C</div></li>
</ul>
const slider = new Slider()
slider.Create({
  slides: document.querySelector('ul')
})
arrows

| Parameter | Required | Type | Default value | | --------- | -------- | --------------------- | ------------- | | arrows | ❌ No | boolean or Arrows | false |

Arrows configuration. You may use a custom Element for the previous and/or next button. Do not use a HTMLButtonElement. You may change the text on the arrows.

<span class="next">Next slide</span>
<span class="prev">Previous slide</span>
const slider = new Slider()
slider.Create({
  // ...
  arrows: true | false | {
    label: 'Slider controls' | undefined,
    next: {
      icon: document.querySelector('.next') | '→' | undefined,
      label: document.querySelector('.next').innerText
    },
    prev: {
      icon: document.querySelector('.prev') | '←' | undefined,
      label: document.querySelector('.prev').innerText
    }
  }
})

See also:

autoplay

| Parameter | Required | Type | Default value | | ---------- | -------- | -------- | ------------- | | autoplay | ❌ No | number | undefined |

Autoplay speed in milliseconds.

Example:

const slider = new Slider()
slider.Create({
  // ...
  autoplay: 4000 | undefined
})
loop

| Parameter | Required | Type | Default value | | --------- | -------- | --------- | ------------- | | loop | ❌ No | boolean | false |

Go from the first to the last slide and the other way around.

Example:

const slider = new Slider()
slider.Create({
  // ...
  loop: true | false
})
caption

| Parameter | Required | Type | Default value | | --------- | -------- | -------- | ------------- | | caption | ❌ No | string | 'Slider' |

Slider caption for screen readers and bots. If empty will use:

  • the [aria-label] attribute text on slides or
  • the innerText of any Element that is referenced with [aria-labelledby] or
  • the [title] attribute text on slides.

Example:

const slider = new Slider()
slider.Create({
  // ...
  caption: 'My Slider' | undefined
})
lang

| Parameter | Required | Type | Default value | | --------- | -------- | -------- | -------------------------------------------------------- | | lang | ❌ No | string | document.documentElement.lang (the documents language) |

Language Code ISO 639-1.

Example:

const slider = new Slider()
slider.Create({
  // ...
  lang: 'de' | 'es' | 'en' | undefined
})
pagination

| Parameter | Required | Type | Default value | | ------------ | -------- | ------------------------- | ------------- | | pagination | ❌ No | boolean or Pagination | false |

Pagination configuration. You can change the label for the pagination element. You can change the innerHTML of the HTMLButtonElement[] elements that are the paginations bullets. Each bullet displays a number by default (starting with '1'). Each Bullet will always have the current index as text for screen readers and bots.

<span class="bullet">⦿</span>
const slider = new Slider()
slider.Create({
  // ...
  pagination: true | false | {
    icon: document.querySelector('.bullet') | '⦿' | undefined,
    label: 'Slider pagination' | undefined
  }
})

See also:

slideBy

| Parameter | Required | Type | Default value | | --------- | -------- | -------- | ------------- | | slideBy | ❌ No | number | 1 |

Number of slides to scroll when using prev/next arrows, arrow keys or autoplay.

Example:

const slider = new Slider()
slider.Create({
  // ...
  slideBy: 2 | undefined
})
syncWith

| Parameter | Required | Type | Default value | | ---------- | -------- | -------- | ------------- | | syncWith | ❌ No | Slider | undefined |

Sync a slider with other sliders.

Example:

const sliderA = new Slider()
const sliderB = new Slider()
const sliderC = new Slider()
sliderA.Create({
  // ...
  syncWith: [sliderB, sliderC]
})
sliderB.Create({
  // ...
  syncWith: [sliderA, sliderC]
})
sliderC.Create({
  // ...
  syncWith: [sliderA, sliderB]
})

See also:

onMoveStart

| Parameter | Required | Type | Default value | | ------------- | -------- | ------------------------ | ------------- | | onMoveStart | ❌ No | (status: Status) => {} | undefined |

Callback function once the sliders movement starts.

Example:

const slider = new Slider()
slider.Create({
  // ...
  onMoveStart: (status) => {
    console.log('Slider has started moving', status)
  } | undefined
})

See also:

onMoveEnd

| Parameter | Required | Type | Default value | | ----------- | -------- | ------------------------ | ------------- | | onMoveEnd | ❌ No | (status: Status) => {} | undefined |

Callback function once the sliders movement stops.

Example:

const slider = new Slider()
slider.Create({
  // ...
  onMoveEnd: (status) => {
    console.log('Slider has stopped moving', status)
  } | undefined
})

See also:

onCreated

| Parameter | Required | Type | Default value | | ----------- | -------- | ---------- | ------------- | | onCreated | ❌ No | () => {} | undefined |

Callback function after the slider was created. Function is called once, when the slider is successfully created. Use this to apply event listeners to elements in any slide (read more).

Example:

const slider = new Slider()
slider.Create({
  // ...
  onCreated: () => {
    console.log('Slider was created')
  } | undefined
})
onDestroyed

| Parameter | Required | Type | Default value | | ------------- | -------- | ---------- | ------------- | | onDestroyed | ❌ No | () => {} | undefined |

Callback function after the slider was destroyed. Function is called once, when the slider is successfully destroyed.

Example:

const slider = new Slider()
slider.Create({
  // ...
  onDestroyed: () => {
    console.log('Slider was destroyed')
  } | undefined
})
onUpdate

| Parameter | Required | Type | Default value | | ---------- | -------- | ------------------------ | ------------- | | onUpdate | ❌ No | (status: Status) => {} | undefined |

Callback function on update. Update is called, whenever the slider changes or moves.

Example:

const slider = new Slider()
slider.Create({
  // ...
  onUpdate: (status) => {
    console.log('Slider is moving or updating', status)
  } | undefined
})

See also:

Method Destroy

Destroy the slider.

mySlider.Destroy()

Once destroyed you can call Method Create again at any time, for example on a window resize event.

Breakpoints

To destroy the slider for a specific breakpoint, whilst maintaining a solid performance, one could use the following code.

myEfficientFn(window, 'resize', () => {
  if(window.innerWidth >= 1024) {
    mySlider.Destroy()
  } else {
    mySlider.Create()
  }
}, 100, true);

myEfficientFn.js

/**
 * Very efficient function with debounce and throttle events
 * @param {HTMLElement} target Target element for eventListener
 * @param {EventListener} eventListener EventListener
 * @param {function} func Callback function
 * @param {number} time Wait for miliseconds after each call
 * @param {boolean} callOnLoad Call func when this function is called
 * @returns {void}
 * @example
 *   myEfficientFn(window, 'scroll', () => { return console.log('hello'); }, 100, true);
 */
function myEfficientFn(target, eventListener, func, time, callOnLoad) {
  /**
   * Throttle function
   * @see {@link https://codeburst.io/throttling-and-debouncing-in-javascript-b01cad5c8edf}
   * @param {function} f Callback function
   * @param {number} w Wait for miliseconds until next execution
   * @returns {function} Calls callback function
   * @example
   *   window.addEventListener('scroll', throttle(() => { return console.log('hello') }, 100));
   */
  function throttle(f, w) {
    /** @type {boolean} Is throtteling */
    let t;

    return function () {
      /** @type {*} Arguments */
      const a = arguments;

      /** @type {*} Context */
      const c = this;

      if (!t) {
        f.apply(c, a);
        t = true;
        setTimeout(() => (t = false), w);
      }
    };
  }

  /**
   * Returns a function, that, as long as it continues to be invoked, will not be triggered. The function will be called after it stops being called for N milliseconds. If `immediate` is passed, trigger the function on the leading edge, instead of the trailing.
   * @see {@link https://davidwalsh.name/javascript-debounce-function}
   * @param {function} f Callback function
   * @param {number} w Wait for miliseconds until next execution
   * @param {boolean} i Do not wait
   * @returns {function} func
   * @example
   *   window.addEventListener('scroll', debounce(() => { console.log('hello'); }, 100));
   */
  function debounce(f, w, i) {
    /** @type {Number|null} Timeout */
    let t;

    return () => {
      /** @type {*} Arguments */
      const a = arguments;

      /** @type {*} Context */
      const c = this;

      const later = () => {
        t = null;
        if (!i) f.apply(c, a);
      };

      /** @type {Boolean} Call now */
      const n = i && !t;
      clearTimeout(t);
      t = setTimeout(later, w);
      if (n) f.apply(c, a);
    };
  }

  // Call on load
  if (callOnLoad) {
    func.call();
  }

  // Throttle
  target.addEventListener(
    eventListener,
    throttle(() => {
      return func.call();
    }, time)
  );

  // Debounce
  target.addEventListener(
    eventListener,
    debounce(() => {
      func.call();
    }, time)
  );
}

Method Prev

Go to previous slide.

mySlider.Prev();

Method Next

Go to next slide.

mySlider.Next();

Method GoTo

Go to a specific slide.

mySlider.GoTo(3); // go to slide 3

GET

Get elements

Get all the sliders HTML-elements.

mySlider.elements.arrowNext // The HTMLButtonElement of the next arrow
mySlider.elements.arrowPrev // The HTMLButtonElement of the previous arrow
mySlider.elements.arrows // The element containing the arrows
mySlider.elements.caption // The element containing the caption
mySlider.elements.items // All slides as list items
mySlider.elements.list // The unordered list element containing all slides
mySlider.elements.pagination // The element containing the pagination
mySlider.elements.paginationBullets // All pagination bullets as HTMLButtonElement

Get config

Get slider configuration.

mySlider.config.arrows // `true` if the slider has arrows
mySlider.config.autoplay // `true` if autoplay is currently active
mySlider.config.autoplaySpeed // Autoplay speed
mySlider.config.caption // The slider caption
mySlider.config.slides // The number of slides
mySlider.config.pagination // `true` if the slider has a pagination

Styles

The slider does not come with any design or theme applied. It has only required functional styles. You may however apply some example styles.

Import

Some functional CSS is required to make the slider work. Make sure to import the SCSS from the node module.

@import "path-to/node_modules/@wanjapflueger/a11y-slider/lib/index";

Space between slides

Overwrite the default value for $space-between-slides from src/partials/slide/_index.scss globally before you import the SCSS.

$space-between-slides: 80px;

@import "path-to/node_modules/@wanjapflueger/a11y-slider/lib/index";

If you do not want to set this value globally but per slider, continue reading Slides per view.

Hide every nth bullet

A single bullet exists for each slide. However if you have lots of slides this will look silly. You can hide some of those bullets like shown in the example below.

[data-a11y-slider] [data-a11y-slider-pagination] > nav > ul > li {
  // Show only every fourth bullet (1, 5, 9 etc.) for tablet
  &:not(:nth-child(4n + 1)) {
    @media (max-width: 767px) {
      display: none;
    }
  }

  // Show only every second bullet (1, 3, 5, etc.) for tablet
  &:not(:nth-child(2n + 1)) {
    @media (min-width: 768px) and (max-width: 1024px) {
      display: none;
    }
  }
}

Slides per view

Use @mixin slide-sizing from src/partials/slide/_index.scss to define how many slides should be visible for each breakpoint.

[data-a11y-slider] {
  @include slide-sizing(1); // Mobile: show 1 slide, space between slides is $space-between-slides

  @media (min-width: 1280px) {
    @include slide-sizing(2.5, 80); // Desktop: show 2 and a half slides, space between slides is 80px
  }
}

Pro Tip: Even if you display only one slide with slide-sizing(1) you can still provide a grid gap.

Tips and Tricks

  • Add a margin when using a box-shadow:

    [data-a11y-slider-slide] {
      margin: 2em 0; // set margin according to the shadows dimensions
    }

Keyboard support

In src/partials/keys.ts several keys have been assigned a function. The keyboard support starts when a slider is active. A slider is active when:

  • the user hovers the slider
  • the focus is on the slider ([data-a11y-slider])
  • any child element inside the slider has the focus ([data-a11y-slider])

The slider will have [aria-current="true"] while active.

| Key | Description | | ------------ | -------------- | | ArrowRight | Next slide | | ArrowLeft | Previous slide | | Digit1 | Slide 1 | | Digit2 | Slide 2 | | Digit3 | Slide 3 | | Digit4 | Slide 4 | | Digit5 | Slide 5 | | Digit6 | Slide 6 | | Digit7 | Slide 7 | | Digit8 | Slide 8 | | Digit9 | Slide 9 |

Dataset attributes

You may overwrite some JavaScript parameters with the following HTML attributes. Set these attributes on your reference element.

  • autoplay

    <div id="my-list" data-a11y-slider-autoplay="4000">
      ...
    </div>
  • lang

    <div id="my-list" data-a11y-slider-lang="de">
      ...
    </div>
  • slideBy

    <div id="my-list" data-a11y-slider-slide-by="2">
      ...
    </div>

Issues

Event Listeners do not work

Issue: https://gitlab.com/wanjapflueger/a11y-slider/-/issues/1

As stated in How to copy a DOM node with event listeners?, event listeners applied to elements within the slider cannot be copied when creating a new slider instance.

To workaround this issue, apply any event listeners in a callback function, that can be passed with the parameter onCreated.

Example:

slider.Create({
  // ...
  onCreated: () => {
    const buttons = document.querySelectorAll('button');
    buttons.forEach((button) => {
      button.addEventListener('click', () => {
        console.log('clicked on a button inside a slide');
      })
    });
  }
  // ...
});

Accessibility Compliance Report

WCAG Level: AAA [^1]

| Browser | Platform | Screen reader | Passed | | -------------------- | ------------- | -------------------------------------------------------------------------- | ------ | | Chrome 90.0.4430.212 | MacOS 10.15.7 | VoiceOver | ✅ | | Chrome 90.0.4430.210 | Android 10 | Talkback | ✅ |


[^1]: This information refers only to the technical aspects of the component, not to the design or the editorial handling of any content.