Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add new option and polyfill with attribute
Add possibility to pass a initialize option with the selector to be used for the polyfill. The polyfill now support attributes and not only classes. BREAKING CHANGE: attributes support fix #11
- Loading branch information
Showing
3 changed files
with
106 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,81 @@ | ||
import { supportsFocusWithin, validClassName } from './utils' | ||
|
||
var focusWithinClass, loaded | ||
import supportsFocusWithin from './utils/supportsFocusWithin' | ||
import addAttribute from './utils/addAttribute' | ||
import removeAttribute from './utils/removeAttribute' | ||
|
||
/** | ||
* Update focus-within class on focus and blur events | ||
* @param {FocusEvent} e | ||
* Load polyfill and return loading state boolean | ||
* | ||
* @param {String} selector | ||
* @returns {Boolean} | ||
* @throws {TypeError} | ||
*/ | ||
function update (e) { | ||
var element, running | ||
|
||
var action = function () { | ||
element = document.activeElement | ||
running = false | ||
function polyfill (selector) { | ||
if (selector) { | ||
// check if selector is a string | ||
if (typeof selector !== 'string') { | ||
throw new TypeError(`Failed to execute '${this.name}' on 'FocusWithin': parameter 1 ('selector') is not a string.')`) | ||
} | ||
|
||
Array.prototype.slice | ||
.call(document.getElementsByClassName(focusWithinClass)) | ||
.forEach(function (el) { el.classList.remove(focusWithinClass) }) | ||
// check if selector is class or attribute | ||
if (selector.charAt(0) !== '.' && (selector.charAt(0) !== '[' && selector.charAt(selector.length - 1) !== ']')) { | ||
throw new TypeError(`Failed to execute '${this.name}' on 'FocusWithin': parameter 1 ('selector') is not a valid selector.')`) | ||
} | ||
|
||
if (e.type === 'focus' && element && element !== document.body) { | ||
for (var el = element; el && el.nodeType === 1; el = el.parentNode) { el.classList.add(focusWithinClass) } | ||
// check if valid selector | ||
try { | ||
document.querySelector(selector) | ||
} catch (error) { | ||
throw new TypeError(`Failed to execute '${this.name}' on 'FocusWithin': parameter 1 ('selector') is not a valid selector.')`) | ||
} | ||
} | ||
|
||
if (!running) { | ||
window.requestAnimationFrame(action) | ||
running = true | ||
} | ||
} | ||
var attributeName, attributeValue, isClass, loaded | ||
|
||
/** | ||
* Load polyfill | ||
* @param {String} className | ||
* @returns {void} | ||
*/ | ||
export function loadPolyfill (className) { | ||
focusWithinClass = className || 'focus-within' | ||
if (!validClassName(focusWithinClass)) { | ||
console.warn('focus-within-polyfill: cannot load. ' + focusWithinClass + ' is not a valid class name') | ||
return | ||
} | ||
selector = selector || '[focus-within]' | ||
isClass = selector.indexOf('.') === 0 | ||
attributeName = !isClass ? selector.replace(/[[\]']+/g, '') : 'class' | ||
attributeValue = !isClass ? attributeName : selector.replace('.', '') | ||
|
||
/** | ||
* - Remove all applied attributes and | ||
* - Add new attributes based on activeElement | ||
* | ||
* @param {FocusEvent} e | ||
*/ | ||
var handler = function (e) { | ||
var element, running | ||
|
||
if (!loaded && !supportsFocusWithin()) { | ||
document.addEventListener('focus', update, true) | ||
document.addEventListener('blur', update, true) | ||
loaded = true | ||
console.info('focus-within-polyfill: loaded.') | ||
var _action = function () { | ||
element = document.activeElement | ||
running = false | ||
|
||
Array.prototype.slice | ||
.call(document.querySelectorAll(selector)) | ||
.forEach(function (el) { removeAttribute(el, attributeName, attributeValue) }) | ||
|
||
if (e.type === 'focus' && element && element !== document.body) { | ||
for (var el = element; el && el.nodeType === 1; el = el.parentNode) { | ||
addAttribute(el, attributeName, attributeValue) | ||
} | ||
} | ||
} | ||
|
||
if (!running) { | ||
window.requestAnimationFrame(_action) | ||
running = true | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Unload polyfill | ||
* @returns {void} | ||
*/ | ||
export function unloadPolyfill () { | ||
if (!loaded) { | ||
console.warn('focus-within-polyfill: cannot unload. Polyfill was never loaded.') | ||
return | ||
// kick off polyfill | ||
loaded = !supportsFocusWithin() | ||
if (loaded) { | ||
document.addEventListener('focus', handler, true) | ||
document.addEventListener('blur', handler, true) | ||
} | ||
|
||
document.removeEventListener('focus', update, true) | ||
document.removeEventListener('blur', update, true) | ||
loaded = false | ||
console.info('focus-within-polyfill: unloaded.') | ||
return loaded | ||
} | ||
|
||
export default { | ||
polyfill: polyfill | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* Add user defined attribute to element retaining any previously applied attributes. | ||
* Attribute can be 'class'. | ||
* | ||
* @param {Element} element | ||
* @param {String} name | ||
* @param {String} value | ||
*/ | ||
function addAttribute (element, name, value) { | ||
var appliedAttributes = element.getAttribute(name) || '' | ||
|
||
if (appliedAttributes.indexOf(value) === -1) { | ||
appliedAttributes = (appliedAttributes + ' ' + value).trim() | ||
element.setAttribute(name, appliedAttributes) | ||
} | ||
} | ||
|
||
export default addAttribute |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/** | ||
* Remove the given attribute value and if no other values are present | ||
* remove the attribute from the Element. | ||
* Attribute can be 'class'. | ||
* | ||
* @param {Element} element | ||
* @param {String} name | ||
* @param {String} value | ||
*/ | ||
function removeAttribute (element, name, value) { | ||
var appliedAttributes = element.getAttribute(name) || '' | ||
|
||
if (appliedAttributes.indexOf(value) !== -1) { | ||
appliedAttributes = appliedAttributes.replace(value, '').trim() | ||
|
||
appliedAttributes !== '' | ||
? element.setAttribute(name, appliedAttributes) | ||
: element.removeAttribute(name) | ||
} | ||
} | ||
|
||
export default removeAttribute |