<html lang="en">
<head>
<meta charset="utf-8">
<title>Mutating the active element</title>
</head>
<body>
<article id="example-introduction">
<h1>Mutating the active element</h1>
<p>
This document provides a simple way to test browsers' behavior when the active element
is mutated so that it isn't focusable anymore. The mutations include hiding and disabling
disabling the element, as well as removing it from the DOM and removing the ability to
receive focus by removing the <code>tabindex</code> attribute. Browser behavior can be
observed for the elements <code><button></code>, <code><input></code> and
<code><div></code>. At the end of the document the current active element is
printed on screen for convenience.
</p>
</article>
<div id="example-html">
<main>
<fieldset>
<legend>Testing grounds</legend>
<p><input id="alpha" value="alpha"></p>
<div id="container" tabindex="-1">
<p><input id="bravo" value="bravo"></p>
<div class="trigger">
<div><button type="button" id="trigger-button">click to mutate</button></div>
<div hidden><input id="trigger-input" value="focus to mutate"></div>
<div hidden><div id="trigger-div" role="button" tabindex="0">click to mutate</div></div>
</div>
<p><input id="charlie" value="charlie"></p>
</div>
</fieldset>
<fieldset id="source">
<legend>Element to mutate</legend>
<p>Choose the type of element you want to observe the mutation on.</p>
<ul>
<li><label><input type="radio" name="source" value="button" id="source-button" checked> button element on click</label></li>
<li><label><input type="radio" name="source" value="input" id="source-input"> input element on focus</label></li>
<li><label><input type="radio" name="source" value="div" id="source-div"> div element on click</label></li>
</ul>
<p><label><input type="checkbox" name="delay" id="delay"> delay mutation for 50ms</label></p>
</fieldset>
<fieldset id="method">
<legend>Method of mutation</legend>
<p>Choose the way the element should lose the ability to receive focus.</p>
<ul>
<li><label><input type="radio" name="method" value="disabled" id="method-disabled" checked> disable element (not div)</label></li>
<li><label><input type="radio" name="method" value="hidden" id="method-hidden"> hide element</label></li>
<li><label><input type="radio" name="method" value="detached" id="method-detached"> detach element</label></li>
<li><label><input type="radio" name="method" value="tabindex" id="method-tabindex" disabled> remove tabindex from element (only div)</label></li>
</ul>
</fieldset>
</main>
<div id="output">document.activeElement: <span id="active-element"></span></div>
</div>
</body>
</html>
var triggers = {
button: document.getElementById('trigger-button'),
input: document.getElementById('trigger-input'),
div: document.getElementById('trigger-div'),
};
var source = document.getElementById('source');
var sources = {
button: document.getElementById('source-button'),
input: document.getElementById('source-input'),
div: document.getElementById('source-div'),
};
var _method = 'disabled';
var method = document.getElementById('method');
var methods = {
disabled: document.getElementById('method-disabled'),
hidden: document.getElementById('method-hidden'),
detached: document.getElementById('method-detached'),
tabindex: document.getElementById('method-tabindex'),
};
var mutations = {
disabled: function(event) {
console.log("disabling element");
event.target.disabled = true;
return function() {
console.log("enabling element");
event.target.disabled = false;
};
},
hidden: function(event) {
console.log("hiding element");
event.target.setAttribute('hidden', '');
return function() {
console.log("showing element");
event.target.removeAttribute('hidden');
};
},
detached: function(event) {
console.log("detaching element");
var element = event.target;
var container = element.parentNode;
var dummy = document.createElement('div');
container.insertBefore(dummy, element);
container.removeChild(element);
return function() {
console.log("attaching element");
container.insertBefore(element, dummy);
container.removeChild(dummy);
};
},
tabindex: function(event) {
console.log("removing tabindex from element");
event.target.removeAttribute('tabindex');
return function() {
console.log("restoring tabindex on element");
event.target.setAttribute('tabindex', '0');
};
},
};
var mutate = mutations.disabled;
source.addEventListener('change', function() {
Object.keys(triggers).forEach(function(key) {
triggers[key].parentNode.hidden = !sources[key].checked;
});
methods.disabled.disabled = sources.div.checked;
methods.tabindex.disabled = !sources.div.checked;
});
method.addEventListener('change', function() {
var checked = this.querySelector(':checked');
mutate = mutations[checked.value];
});
var delay = document.getElementById('delay');
function handleMutation(event) {
var executeMutation = function() {
var reset = mutate(event);
reset && setTimeout(reset, 1000);
};
if (delay) {
setTimeout(executeMutation, 50);
} else {
executeMutation();
}
};
triggers.button.addEventListener('click', handleMutation, false);
triggers.input.addEventListener('focus', handleMutation, false);
triggers.div.addEventListener('click', handleMutation, false);
triggers.div.addEventListener('keyup', function(event) {
if (event.keyCode !== 13 && event.keyCode !== 32) {
return;
}
handleMutation(event);
}, false);
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback) {
setTimeout(callback, 16);
};
}
var activeElement = document.getElementById('active-element');
var previousActiveElement = document.getActiveElement;
function updateActiveElement() {
requestAnimationFrame(updateActiveElement);
if (previousActiveElement === document.activeElement) {
return;
}
var ident = document.activeElement && (document.activeElement.id || document.activeElement.nodeName.toLowerCase()) || 'null';
activeElement.textContent = ident;
previousActiveElement = document.activeElement;
}
updateActiveElement();
Output
300px
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |