diff --git a/.gitignore b/.gitignore index ac52de0..430ab05 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -# rake standalone -standalone/flying-focus.js - # rake safari FlyingFocus.safariextension/flying-focus.css FlyingFocus.safariextension/flying-focus.js diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..391efae --- /dev/null +++ b/bower.json @@ -0,0 +1,27 @@ +{ + "name": "flying-focus", + "version": "1.2.1", + "homepage": "http://n12v.com/focus-transition/", + "authors": [ + "Nikita Vasilyev " + ], + "description": "Adds a transition to the focus outline when you tab around inputs, buttons, and links.", + "main": "standalone/flying-focus.js", + "keywords": [ + "focus", + "UI", + "UX", + "accessibility", + "animation" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "chrome/", + "FlyingFocus.safariextension/", + "standalone/flying-focus.jspp.js", + "test", + "art", + "Rakefile" + ] +} diff --git a/standalone/flying-focus.js b/standalone/flying-focus.js new file mode 100644 index 0000000..c3faec2 --- /dev/null +++ b/standalone/flying-focus.js @@ -0,0 +1,128 @@ +document.addEventListener('DOMContentLoaded', function() { + if (document.getElementById('flying-focus')) return; + + var flyingFocus = document.createElement('flying-focus'); // use uniq element name to decrease the chances of a conflict with website styles +flyingFocus.id = 'flying-focus'; +document.body.appendChild(flyingFocus); + +var DURATION = 100; +flyingFocus.style.transitionDuration = flyingFocus.style.WebkitTransitionDuration = DURATION / 1000 + 's'; + +function offsetOf(elem) { + var rect = elem.getBoundingClientRect(); + var docElem = document.documentElement; + var win = document.defaultView; + var body = document.body; + + var clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + scrollTop = win.pageYOffset || docElem.scrollTop || body.scrollTop, + scrollLeft = win.pageXOffset || docElem.scrollLeft || body.scrollLeft, + top = rect.top + scrollTop - clientTop, + left = rect.left + scrollLeft - clientLeft; + + return {top: top, left: left}; +} + +var movingId = 0; +var prevFocused = null; +var isFirstFocus = true; +var keyDownTime = 0; + +document.documentElement.addEventListener('keydown', function(event) { + var code = event.which; + // Show animation only upon Tab or Arrow keys press. + if (code === 9 || (code > 36 && code < 41)) { + keyDownTime = now(); + } +}, false); + +document.documentElement.addEventListener('focus', function(event) { + var target = event.target; + if (target.id === 'flying-focus') { + return; + } + var offset = offsetOf(target); + flyingFocus.style.left = offset.left + 'px'; + flyingFocus.style.top = offset.top + 'px'; + flyingFocus.style.width = target.offsetWidth + 'px'; + flyingFocus.style.height = target.offsetHeight + 'px'; + + // Would be nice to use: + // + // flyingFocus.style['outline-offset'] = getComputedStyle(target, null)['outline-offset'] + // + // but it always '0px' in WebKit and Blink for some reason :( + + if (isFirstFocus) { + isFirstFocus = false; + return; + } + + if (now() - keyDownTime > 42) { + return; + } + + onEnd(); + target.classList.add('flying-focus_target'); + flyingFocus.classList.add('flying-focus_visible'); + prevFocused = target; + movingId = setTimeout(onEnd, DURATION); +}, true); + +document.documentElement.addEventListener('blur', function() { + onEnd(); +}, true); + + +function onEnd() { + if (!movingId) { + return; + } + clearTimeout(movingId); + movingId = 0; + flyingFocus.classList.remove('flying-focus_visible'); + prevFocused.classList.remove('flying-focus_target'); + prevFocused = null; +} + +function now() { + return new Date().valueOf(); +} + + + var style = document.createElement('style'); + style.textContent = "#flying-focus {\ + position: absolute;\ + margin: 0;\ + background: transparent;\ + -webkit-transition-property: left, top, width, height;\ + transition-property: left, top, width, height;\ + -webkit-transition-timing-function: cubic-bezier(0, 0.2, 0, 1);\ + transition-timing-function: cubic-bezier(0, 0.2, 0, 1);\ + visibility: hidden;\ + pointer-events: none;\ + box-shadow: 0 0 2px 3px #78aeda, 0 0 2px #78aeda inset; border-radius: 2px;\ +}\ +#flying-focus.flying-focus_visible {\ + visibility: visible;\ + z-index: 9999;\ +}\ +.flying-focus_target {\ + outline: none !important; /* Doesn't work in Firefox :( */\ +}\ +/* http://stackoverflow.com/questions/71074/how-to-remove-firefoxs-dotted-outline-on-buttons-as-well-as-links/199319 */\ +.flying-focus_target::-moz-focus-inner {\ + border: 0 !important;\ +}\ +/* Replace it with @supports rule when browsers catch up */\ +@media screen and (-webkit-min-device-pixel-ratio: 0) {\ + #flying-focus {\ + box-shadow: none;\ + outline: 5px auto -webkit-focus-ring-color;\ + outline-offset: -3px;\ + }\ +}\ +"; + document.body.appendChild(style); +}, false); diff --git a/standalone/flying-focus.jspp.js b/standalone/flying-focus.jspp.js index bfa735b..ddaf61d 100644 --- a/standalone/flying-focus.jspp.js +++ b/standalone/flying-focus.jspp.js @@ -1,4 +1,4 @@ -document.addEventListener('DOMContentLoaded', function(e) { +document.addEventListener('DOMContentLoaded', function() { if (document.getElementById('flying-focus')) return; /*> ../chrome/flying-focus.js */