Skip to content

Commit

Permalink
feature(element/blur): interface to remove focus from an element
Browse files Browse the repository at this point in the history
  • Loading branch information
rodneyrehm committed Mar 31, 2016
1 parent bc13efd commit 707fe88
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/api/README.md
Expand Up @@ -64,6 +64,7 @@ Unlike any other ally modules, these components do not take take [`options.conte

Making up for missing or lacking DOM mutation APIs.

* [`ally.element.blur`](element/blur.md) shifts focus away from an element
* [`ally.element.disabled`](element/disabled.md) disables all elements, not only form controls
* [`ally.element.focus`](element/focus.md) shifts focus to an element

Expand Down
60 changes: 60 additions & 0 deletions docs/api/element/blur.md
@@ -0,0 +1,60 @@
---
layout: doc-api.html
tags: argument-list
---

# ally.element.blur

Shifts focus away from an element if it currently has focus.


## Description

The [`blur()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur) method is available on all [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement). But this is not necessarily true for [`SVGElement`](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement). Only Blink and WebKit expose the `blur()` 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.blur()` tries to apply `HTMLElement.prototoype.blur` to `SVGElement`s. This only works for Internet Explorer 9 - 11, as Gecko and Edge actively prevent this.

According to [jQuery Bug 9420](https://bugs.jqueryui.com/ticket/9420) calling `blur()` on the `body` element may make the browser window lose focus. `ally.element.blur()` guards against this.


## Usage

```js
var element = document.getElementById('victim');
var result = ally.element.blur(element);
```

### Arguments

| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| element | [`<selector>`](../concepts.md#Selector) | *required* | The Element to blur. First element of the collections is used. |

### Returns

[`HTMLElement`](https://developer.mozilla.org/en/docs/Web/API/HTMLElement) that received focus (usually `document.body`) or `null` if focus could not be shifted.

### Throws

[`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) if `element` argument does not resolve to an `HTMLElement`.


## Examples


## Changes

* Added in `v#master`.


## Notes


## Related resources

* [`ally.element.focus`](./focus.md) shifts focus to an element if it does not already have focus.


## Contributing

* [module source](https://github.com/medialize/ally.js/blob/master/src/element/blur.js)
* [document source](https://github.com/medialize/ally.js/blob/master/docs/api/element/blur.md)
* [unit test](https://github.com/medialize/ally.js/blob/master/test/unit/element.blur.test.js)
2 changes: 2 additions & 0 deletions docs/api/element/focus.md
Expand Up @@ -56,6 +56,8 @@ var result = ally.element.focus(element);

## Related resources

* [`ally.element.blur`](./blur.md) shifts focus away from an element if it currently has focus.


## Contributing

Expand Down
2 changes: 2 additions & 0 deletions src/element/_element.js
@@ -1,9 +1,11 @@

// exporting modules to be included the UMD bundle

import blur from './blur';
import disabled from './disabled';
import focus from './focus';
export default {
blur,
disabled,
focus,
};
43 changes: 43 additions & 0 deletions src/element/blur.js
@@ -0,0 +1,43 @@

import isActiveElement from '../is/active-element';
import contextToElement from '../util/context-to-element';
import getWindow from '../util/get-window';

export default function(context) {
const element = contextToElement({
label: 'element/blur',
context,
});

if (!isActiveElement(element)) {
return null;
}

const nodeName = element.nodeName.toLowerCase();
if (nodeName === 'body') {
// prevent the browser window from losing focus in IE9
// according to https://bugs.jqueryui.com/ticket/9420
return null;
}

if (element.blur) {
element.blur();
return document.activeElement;
}

const _window = getWindow(element);

try {
// The element itself does not have a blur method.
// This is true for SVG elements in Firefox and IE,
// as well as MathML elements in every browser.
// IE9 - 11 will let us abuse HTMLElement's blur method,
// Firefox and Edge will throw an error.
_window.HTMLElement.prototype.focus.call(element);
return document.activeElement;
} catch (e) {
// we may want to try focusing <body> before giving up.
// not sure how this works for an SVG document, though.
return null;
}
}
1 change: 1 addition & 0 deletions test/intern.js
Expand Up @@ -78,6 +78,7 @@ define([

// Non-functional test suite(s) to run in each browser
suites: [
'test/unit/element.blur.test',
'test/unit/element.disabled.test',
'test/unit/element.focus.test',
'test/unit/event.active-element.test',
Expand Down
3 changes: 2 additions & 1 deletion test/unit/core.worker.test.js
Expand Up @@ -11,7 +11,6 @@ define([
// we'll load the ones with the fewest dependencies first,
// so we don't hit timeouts caused by slow networks or VMs
var modules = [
'ally.js/element/disabled',
'ally.js/event/active-element',
'ally.js/event/shadow-focus',
'ally.js/prototype/element.prototype.matches',
Expand Down Expand Up @@ -41,6 +40,8 @@ define([
'ally.js/get/parents',
'ally.js/get/shadow-host-parents',
'ally.js/get/shadow-host',
'ally.js/element/blur',
'ally.js/element/disabled',
'ally.js/element/focus',
'ally.js/observe/interaction-type',
'ally.js/observe/shadow-mutations',
Expand Down
63 changes: 63 additions & 0 deletions test/unit/element.blur.test.js
@@ -0,0 +1,63 @@
define([
'intern!object',
'intern/chai!expect',
'../helper/fixtures/focusable.fixture',
'../helper/supports',
'ally/util/platform',
'ally/element/focus',
'ally/element/blur',
], function(registerSuite, expect, focusableFixture, supports, platform, elementFocus, elementBlur) {

registerSuite(function() {
var fixture;

return {
name: 'element/blur',

beforeEach: function() {
fixture = focusableFixture();
},
afterEach: function() {
fixture.remove();
fixture = null;
},

invalid: function() {
expect(function() {
elementBlur(null);
}).to.throw(TypeError, 'element/blur requires valid options.context');
},

'non focusable element': function() {
var element = document.getElementById('inert-div');
var result = elementBlur(element);
expect(result).to.equal(null);
},
'not active element': function() {
var activeElement = document.getElementById('tabindex-1');
activeElement.focus();

var element = document.getElementById('tabindex-0');
var result = elementBlur(element);
expect(result).to.equal(null);
},
'blur active element': function() {
var element = document.getElementById('tabindex-0');
element.focus();

var result = elementBlur(element);
expect(result).to.equal(document.body);
},
'blur body element': function() {
var result = elementBlur(document.body);
expect(result).to.equal(null);
},
'focusable svg element': function() {
var element = document.getElementById('svg-link');
var result = elementFocus(element);
var canFocusSvg = supports.svgFocusMethod || platform.is.TRIDENT && platform.majorVersion < 13;
expect(result).to.equal(canFocusSvg ? element : null);
},
};
});
});

0 comments on commit 707fe88

Please sign in to comment.