Skip to content

Commit

Permalink
feature(element.focus): adding options.undoScrolling - #138
Browse files Browse the repository at this point in the history
  • Loading branch information
rodneyrehm committed Aug 31, 2016
1 parent 2a44277 commit 043e4fc
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/api/element/focus.md
Expand Up @@ -20,6 +20,7 @@ The [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/foc

While `focus()` is available on `HTMLElement`, this is not necessarily true for [`SVGElement`](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement). Only Blink and WebKit expose the `focus()` method on SVG elements, Gecko, Trident and Edge do not. This will likely change once [SVG 2](http://www.w3.org/TR/SVG2/interact.html#Focus) is a thing. Until then `ally.element.focus()` tries to apply `HTMLElement.prototoype.focus` to `SVGElement`s. This only works for Internet Explorer 9 - 11, as Gecko and Edge actively prevent this.

Browsers [scroll the focused element into view](https://github.com/whatwg/html/issues/94), if it isn't already. However, this may interfere with widgets revealing content in an animated fashion. As there is no way to focus an element *without* the browser scrolling the element into view, `ally.element.focus()` provides the option `undoScrolling` to revert all scroll containers of the focused element to their state before the element got focus.

## Usage

Expand All @@ -40,6 +41,7 @@ var result = ally.element.focus(element);
| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| defaultToAncestor | boolean | `false` | If the Element itself is not focusable the first focusable element in its ancestry is used instead. |
| undoScrolling | boolean | `false` | Revert the browser's native scrollIntoView upon focus behavior. |


### Returns
Expand Down
13 changes: 12 additions & 1 deletion src/element/focus.js
Expand Up @@ -6,6 +6,7 @@ import isActiveElement from '../is/active-element';
import isFocusable from '../is/focusable';
import contextToElement from '../util/context-to-element';
import getWindow from '../util/get-window';
import resetScrolling from '../util/reset-scrolling';

function focus(element) {
if (element.focus) {
Expand Down Expand Up @@ -40,6 +41,7 @@ const except = {

export default function(context, {
defaultToAncestor,
undoScrolling,
} = {}) {
const element = contextToElement({
label: 'element/focus',
Expand Down Expand Up @@ -68,5 +70,14 @@ export default function(context, {
return target;
}

return focus(target);
let _undoScrolling;
if (undoScrolling) {
_undoScrolling = resetScrolling(target);
}

const result = focus(target);

_undoScrolling && _undoScrolling();

return result;
}
37 changes: 37 additions & 0 deletions test/unit/element.focus.test.js
Expand Up @@ -8,6 +8,7 @@ define(function(require) {
var supports = require('../helper/supports');
var platform = require('ally/util/platform');
var elementFocus = require('ally/element/focus');
var visibleArea = require('ally/util/visible-area');

bdd.describe('element/focus', function() {
var fixture;
Expand Down Expand Up @@ -99,5 +100,41 @@ define(function(require) {
});
});

bdd.describe('for option.undoScrolling', function() {
bdd.beforeEach(function() {
// shift #link-tabindex--1 out of sight
var container = document.querySelector('.context');
var spacer = document.getElementById('link');

container.setAttribute('style', 'overflow: auto; height: 100px;');
spacer.setAttribute('style', 'margin: 100px 0; display: block;');
});

bdd.it('should by scroll focused element into view by default', function() {
var target = document.getElementById('link-tabindex--1');

expect(visibleArea(target)).to.equal(0);

var result = elementFocus(target);

expect(result).to.equal(target);
// Trident scrolls the element to about 98% visibility
expect(visibleArea(target)).to.be.at.least(0.9);
});

bdd.it('should revert scroll positions after focusing element', function() {
var target = document.getElementById('link-tabindex--1');

expect(visibleArea(target)).to.equal(0);

var result = elementFocus(target, {
undoScrolling: true,
});

expect(result).to.equal(target);
expect(visibleArea(target)).to.equal(0);
});
});

});
});

0 comments on commit 043e4fc

Please sign in to comment.