UNPKG

details-element-polyfill

Version:
195 lines (194 loc) 6.95 kB
/* Details Element Polyfill 2.4.0 Copyright © 2019 Javan Makhmali */ (function() { "use strict"; var element = document.createElement("details"); var elementIsNative = typeof HTMLDetailsElement != "undefined" && element instanceof HTMLDetailsElement; var support = { open: "open" in element || elementIsNative, toggle: "ontoggle" in element }; var styles = '\ndetails, summary {\n display: block;\n}\ndetails:not([open]) > *:not(summary) {\n display: none;\n}\nsummary::before {\n content: "►";\n padding-right: 0.3rem;\n font-size: 0.6rem;\n cursor: default;\n}\n[open] > summary::before {\n content: "▼";\n}\n'; var _ref = [], forEach = _ref.forEach, slice = _ref.slice; if (!support.open) { polyfillStyles(); polyfillProperties(); polyfillToggle(); polyfillAccessibility(); } if (support.open && !support.toggle) { polyfillToggleEvent(); } function polyfillStyles() { document.head.insertAdjacentHTML("afterbegin", "<style>" + styles + "</style>"); } function polyfillProperties() { var prototype = document.createElement("details").constructor.prototype; var setAttribute = prototype.setAttribute, removeAttribute = prototype.removeAttribute; var open = Object.getOwnPropertyDescriptor(prototype, "open"); Object.defineProperties(prototype, { open: { get: function get() { if (this.tagName == "DETAILS") { return this.hasAttribute("open"); } else { if (open && open.get) { return open.get.call(this); } } }, set: function set(value) { if (this.tagName == "DETAILS") { return value ? this.setAttribute("open", "") : this.removeAttribute("open"); } else { if (open && open.set) { return open.set.call(this, value); } } } }, setAttribute: { value: function value(name, _value) { var _this = this; var call = function call() { return setAttribute.call(_this, name, _value); }; if (name == "open" && this.tagName == "DETAILS") { var wasOpen = this.hasAttribute("open"); var result = call(); if (!wasOpen) { var summary = this.querySelector("summary"); if (summary) summary.setAttribute("aria-expanded", true); triggerToggle(this); } return result; } return call(); } }, removeAttribute: { value: function value(name) { var _this2 = this; var call = function call() { return removeAttribute.call(_this2, name); }; if (name == "open" && this.tagName == "DETAILS") { var wasOpen = this.hasAttribute("open"); var result = call(); if (wasOpen) { var summary = this.querySelector("summary"); if (summary) summary.setAttribute("aria-expanded", false); triggerToggle(this); } return result; } return call(); } } }); } function polyfillToggle() { onTogglingTrigger(function(element) { element.hasAttribute("open") ? element.removeAttribute("open") : element.setAttribute("open", ""); }); } function polyfillToggleEvent() { if (window.MutationObserver) { new MutationObserver(function(mutations) { forEach.call(mutations, function(mutation) { var target = mutation.target, attributeName = mutation.attributeName; if (target.tagName == "DETAILS" && attributeName == "open") { triggerToggle(target); } }); }).observe(document.documentElement, { attributes: true, subtree: true }); } else { onTogglingTrigger(function(element) { var wasOpen = element.getAttribute("open"); setTimeout(function() { var isOpen = element.getAttribute("open"); if (wasOpen != isOpen) { triggerToggle(element); } }, 1); }); } } function polyfillAccessibility() { setAccessibilityAttributes(document); if (window.MutationObserver) { new MutationObserver(function(mutations) { forEach.call(mutations, function(mutation) { forEach.call(mutation.addedNodes, setAccessibilityAttributes); }); }).observe(document.documentElement, { subtree: true, childList: true }); } else { document.addEventListener("DOMNodeInserted", function(event) { setAccessibilityAttributes(event.target); }); } } function setAccessibilityAttributes(root) { findElementsWithTagName(root, "SUMMARY").forEach(function(summary) { var details = findClosestElementWithTagName(summary, "DETAILS"); summary.setAttribute("aria-expanded", details.hasAttribute("open")); if (!summary.hasAttribute("tabindex")) summary.setAttribute("tabindex", "0"); if (!summary.hasAttribute("role")) summary.setAttribute("role", "button"); }); } function eventIsSignificant(event) { return !(event.defaultPrevented || event.ctrlKey || event.metaKey || event.shiftKey || event.target.isContentEditable); } function onTogglingTrigger(callback) { addEventListener("click", function(event) { if (eventIsSignificant(event)) { if (event.which <= 1) { var element = findClosestElementWithTagName(event.target, "SUMMARY"); if (element && element.parentNode && element.parentNode.tagName == "DETAILS") { callback(element.parentNode); } } } }, false); addEventListener("keydown", function(event) { if (eventIsSignificant(event)) { if (event.keyCode == 13 || event.keyCode == 32) { var element = findClosestElementWithTagName(event.target, "SUMMARY"); if (element && element.parentNode && element.parentNode.tagName == "DETAILS") { callback(element.parentNode); event.preventDefault(); } } } }, false); } function triggerToggle(element) { var event = document.createEvent("Event"); event.initEvent("toggle", false, false); element.dispatchEvent(event); } function findElementsWithTagName(root, tagName) { return (root.tagName == tagName ? [ root ] : []).concat(typeof root.getElementsByTagName == "function" ? slice.call(root.getElementsByTagName(tagName)) : []); } function findClosestElementWithTagName(element, tagName) { if (typeof element.closest == "function") { return element.closest(tagName); } else { while (element) { if (element.tagName == tagName) { return element; } else { element = element.parentNode; } } } } })();