Skip to content

Commit

Permalink
Merge pull request #12 from edenspiekermann/feature/multi-toggles
Browse files Browse the repository at this point in the history
[RFR] Added support for synced multi toggles
  • Loading branch information
KittyGiraudel committed Mar 16, 2016
2 parents 4655919 + ecc116e commit 8ff009c
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 132 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Then add this in your stylesheet (feel free to scope or restrict it):

## Tests

[CasperJS](http://casperjs.org) is being used to run browser tests. CasperJS has some [dependencies](http://docs.casperjs.org/en/latest/installation.html#prerequisites) that have to be installed manually. Be sure to satisfy them before running the tests.
[Mocha](https://mochajs.org/) and [expect.js](https://github.com/Automattic/expect.js) are used to run browser tests.

```
npm test
Expand Down
77 changes: 35 additions & 42 deletions a11y-toggle.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
(function () {
// Simple polyfill for Object.keys
function objectKeys (object) {
if (Object.keys) return Object.keys(object);
'use strict';

var keys = [];
for (var key in object) object.hasOwnProperty(key) && keys.push(key);
return keys;
var internalId = 0;

function $ (selector) {
return Array.prototype.slice.call(document.querySelectorAll(selector));
}

function closest(el, selector) {
Expand All @@ -19,52 +18,46 @@
return null;
}

var namespace = 'data-a11y-toggle';
var toggles = document.querySelectorAll('[' + namespace + ']');
var togglesMap = {};
var togglesMap = $('[data-a11y-toggle]').reduce(function (acc, toggle) {
var targetId = toggle.getAttribute('data-a11y-toggle');

// Loop over the toggles
for (var i = 0; i < toggles.length; i += 1) {
var toggle = toggles[i];
var targetId = toggle.getAttribute(namespace);
toggle.id || toggle.setAttribute('id', 'a11y-toggle-' + internalId++);
toggle.hasAttribute('aria-expanded') || toggle.setAttribute('aria-expanded', true);
toggle.setAttribute('aria-controls', targetId);

// Set `id`, `aria-expanded` and `aria-controls` if not set yet
toggle.id || toggle.setAttribute('id', targetId + '-a11y-toggle');
toggle.hasAttribute('aria-expanded') || toggle.setAttribute('aria-expanded', false);
toggle.hasAttribute('aria-controls') || toggle.setAttribute('aria-controls', targetId);
acc['#' + targetId] = acc['#' + targetId] || [];
acc['#' + targetId].push(toggle);

// Map target `id` selector with toggle `id`
togglesMap['#' + targetId] = toggle.id;
}
return acc;
}, {});

var targetsList = objectKeys(togglesMap);
var targets = targetsList.length && document.querySelectorAll(targetsList);
var targetsMap = {};
var targetsMap = $(Object.keys(togglesMap)).reduce(function (acc, target) {
var labelledby = togglesMap['#' + target.id].map(function (toggle) {
return toggle.id;
}).join(' ');

// Loop over targets
for (var j = 0; j < targets.length; j += 1) {
var target = targets[j];
var toggleId = togglesMap['#' + target.id];
target.hasAttribute('aria-hidden') || target.setAttribute('aria-hidden', false);
target.hasAttribute('aria-labelledby') || target.setAttribute('aria-labelledby', labelledby);

// Set `aria-hidden` and `aria-labelledby` if not set yet
target.hasAttribute('aria-hidden') || target.setAttribute('aria-hidden', true);
target.hasAttribute('aria-labelledby') || (toggleId && target.setAttribute('aria-labelledby', toggleId));
acc[target.id] = target;

// Map target `id` with target node for quick find on click event
targetsMap[target.id] = target;
}
return acc;
}, {});

document.addEventListener('click', function (event) {
var toggle = event.target.hasAttribute(namespace)
var toggle = event.target.hasAttribute('data-a11y-toggle')
? event.target
: closest(event.target, '[' + namespace + ']');
var target = toggle && targetsMap[toggle.getAttribute(namespace)];
: closest(event.target, '[data-a11y-toggle]');
var target = toggle && targetsMap[toggle.getAttribute('aria-controls')];

if (target) {
var isExpanded = toggle.getAttribute('aria-expanded') === 'true';
if (!target) return false;

toggle.setAttribute('aria-expanded', !isExpanded);
target.setAttribute('aria-hidden', isExpanded);
}
var isExpanded = target.getAttribute('aria-hidden') === 'false';
var toggles = togglesMap['#' + target.id];

target.setAttribute('aria-hidden', isExpanded);
toggles.forEach(function (toggle) {
return toggle.setAttribute('aria-expanded', !isExpanded);
});
});
}());
})();
2 changes: 1 addition & 1 deletion a11y-toggle.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 35 additions & 42 deletions example/main.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
(function () {
// Simple polyfill for Object.keys
function objectKeys (object) {
if (Object.keys) return Object.keys(object);
'use strict';

var keys = [];
for (var key in object) object.hasOwnProperty(key) && keys.push(key);
return keys;
var internalId = 0;

function $ (selector) {
return Array.prototype.slice.call(document.querySelectorAll(selector));
}

function closest(el, selector) {
Expand All @@ -19,52 +18,46 @@
return null;
}

var namespace = 'data-a11y-toggle';
var toggles = document.querySelectorAll('[' + namespace + ']');
var togglesMap = {};
var togglesMap = $('[data-a11y-toggle]').reduce(function (acc, toggle) {
var targetId = toggle.getAttribute('data-a11y-toggle');

// Loop over the toggles
for (var i = 0; i < toggles.length; i += 1) {
var toggle = toggles[i];
var targetId = toggle.getAttribute(namespace);
toggle.id || toggle.setAttribute('id', 'a11y-toggle-' + internalId++);
toggle.hasAttribute('aria-expanded') || toggle.setAttribute('aria-expanded', true);
toggle.setAttribute('aria-controls', targetId);

// Set `id`, `aria-expanded` and `aria-controls` if not set yet
toggle.id || toggle.setAttribute('id', targetId + '-a11y-toggle');
toggle.hasAttribute('aria-expanded') || toggle.setAttribute('aria-expanded', false);
toggle.hasAttribute('aria-controls') || toggle.setAttribute('aria-controls', targetId);
acc['#' + targetId] = acc['#' + targetId] || [];
acc['#' + targetId].push(toggle);

// Map target `id` selector with toggle `id`
togglesMap['#' + targetId] = toggle.id;
}
return acc;
}, {});

var targetsList = objectKeys(togglesMap);
var targets = targetsList.length && document.querySelectorAll(targetsList);
var targetsMap = {};
var targetsMap = $(Object.keys(togglesMap)).reduce(function (acc, target) {
var labelledby = togglesMap['#' + target.id].map(function (toggle) {
return toggle.id;
}).join(' ');

// Loop over targets
for (var j = 0; j < targets.length; j += 1) {
var target = targets[j];
var toggleId = togglesMap['#' + target.id];
target.hasAttribute('aria-hidden') || target.setAttribute('aria-hidden', false);
target.hasAttribute('aria-labelledby') || target.setAttribute('aria-labelledby', labelledby);

// Set `aria-hidden` and `aria-labelledby` if not set yet
target.hasAttribute('aria-hidden') || target.setAttribute('aria-hidden', true);
target.hasAttribute('aria-labelledby') || (toggleId && target.setAttribute('aria-labelledby', toggleId));
acc[target.id] = target;

// Map target `id` with target node for quick find on click event
targetsMap[target.id] = target;
}
return acc;
}, {});

document.addEventListener('click', function (event) {
var toggle = event.target.hasAttribute(namespace)
var toggle = event.target.hasAttribute('data-a11y-toggle')
? event.target
: closest(event.target, '[' + namespace + ']');
var target = toggle && targetsMap[toggle.getAttribute(namespace)];
: closest(event.target, '[data-a11y-toggle]');
var target = toggle && targetsMap[toggle.getAttribute('aria-controls')];

if (target) {
var isExpanded = toggle.getAttribute('aria-expanded') === 'true';
if (!target) return false;

toggle.setAttribute('aria-expanded', !isExpanded);
target.setAttribute('aria-hidden', isExpanded);
}
var isExpanded = target.getAttribute('aria-hidden') === 'false';
var toggles = togglesMap['#' + target.id];

target.setAttribute('aria-hidden', isExpanded);
toggles.forEach(function (toggle) {
return toggle.setAttribute('aria-expanded', !isExpanded);
});
});
}());
})();
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
"predeploy": "npm run build",
"deploy": "git subtree push --prefix example origin gh-pages",
"lint": "semistandard a11y-toggle.js",
"test": "casperjs test tests/index.js"
"test": "open tests/index.html"
},
"devDependencies": {
"casperjs": "^1.1.0-beta5",
"mocha": "^2.4.5",
"semistandard": "^7.0.5",
"uglify-js": "^2.6.1"
}
Expand Down
99 changes: 99 additions & 0 deletions tests/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="en">

<head>
<style>.test-suite, [aria-hidden="true"] { display: none; }</style>
<meta charset="utf-8">
<title>Mocha Tests</title>
<link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />
</head>

<body>
<div id="mocha"></div>

<div class="test-suite">
<div class="test test-0">
<button data-a11y-toggle="t0" type="button">Toggle</button>
<div id="t0">Target</div>
</div>

<div class="test test-1">
<button data-a11y-toggle="t1" type="button" id="custom-id-1">Toggle</button>
<div id="t1">Target</div>
</div>

<div class="test test-2">
<button data-a11y-toggle="t2" type="button">Toggle</button>
<div id="t2">Target</div>
</div>

<div class="test test-3">
<button data-a11y-toggle="t3" type="button" aria-expanded="false">Toggle</button>
<div id="t3">Target</div>
</div>

<div class="test test-4">
<button data-a11y-toggle="t4" type="button">Toggle</button>
<div id="t4">Target</div>
</div>

<div class="test test-5">
<button data-a11y-toggle="t5" type="button">Toggle</button>
<div id="t5">Target</div>
</div>

<div class="test test-6">
<button data-a11y-toggle="t6" type="button" id="toggle-t6" aria-expanded="false">Toggle</button>
<div id="t6" aria-hidden="true">Target</div>
</div>

<div class="test test-7">
<button data-a11y-toggle="t7" type="button" id="custom-id-2">Toggle</button>
<div id="t7">Target</div>
</div>

<div class="test test-8">
<button data-a11y-toggle="t8" type="button" id="custom-id-3">Toggle</button>
<button data-a11y-toggle="t8" type="button" id="custom-id-4">Toggle</button>
<div id="t8">Target</div>
</div>

<div class="test test-9">
<button data-a11y-toggle="t9" type="button" id="custom-id-5">Toggle</button>
<div id="t9" aria-labelledby="custom-id-5">Target</div>
</div>

<div class="test test-10">
<button data-a11y-toggle="t10" type="button">Toggle</button>
<div id="t10">Target</div>
</div>

<div class="test test-11">
<button data-a11y-toggle="t11" type="button">Toggle</button>
<div id="t11">Target</div>
</div>

<div class="test test-12">
<button data-a11y-toggle="t12" type="button">Toggle</button>
<button data-a11y-toggle="t12" type="button" id="custom-id-6">Toggle</button>
<div id="t12">Target</div>
</div>

<div class="test test-13">
<button data-a11y-toggle="t13" type="button"><span>Toggle</span></button>
<div id="t13">Target</div>
</div>
</div>

<script src="../a11y-toggle.js"></script>
<script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>
<script src="https://cdn.rawgit.com/Automattic/expect.js/0.3.1/index.js"></script>
<script>mocha.setup('bdd')</script>
<script src="./index.js"></script>
<script>
mocha.checkLeaks();
mocha.run();
</script>
</body>

</html>
Loading

0 comments on commit 8ff009c

Please sign in to comment.