| /** | |
| * Note that this script is intended to be included at the *end* of the document, before </body> | |
| */ | |
| (function (window, document) { | |
| if ('open' in document.createElement('details')) return; | |
| // made global by myself to be reused elsewhere | |
| var addEvent = (function () { | |
| if (document.addEventListener) { | |
| return function (el, type, fn) { | |
| if (el && el.nodeName || el === window) { | |
| el.addEventListener(type, fn, false); | |
| } else if (el && el.length) { | |
| for (var i = 0; i < el.length; i++) { | |
| addEvent(el[i], type, fn); | |
| } | |
| } | |
| }; | |
| } else { | |
| return function (el, type, fn) { | |
| if (el && el.nodeName || el === window) { | |
| el.attachEvent('on' + type, function () { return fn.call(el, window.event); }); | |
| } else if (el && el.length) { | |
| for (var i = 0; i < el.length; i++) { | |
| addEvent(el[i], type, fn); | |
| } | |
| } | |
| }; | |
| } | |
| })(); | |
| /** details support - typically in it's own script */ | |
| // find the first /real/ node | |
| function firstNode(source) { | |
| var node = null; | |
| if (source.firstChild.nodeName != "#text") { | |
| return source.firstChild; | |
| } else { | |
| source = source.firstChild; | |
| do { | |
| source = source.nextSibling; | |
| } while (source && source.nodeName == '#text'); | |
| return source || null; | |
| } | |
| } | |
| function isSummary(el) { | |
| var nn = el.nodeName.toUpperCase(); | |
| if (nn == 'DETAILS') { | |
| return false; | |
| } else if (nn == 'SUMMARY') { | |
| return true; | |
| } else { | |
| return isSummary(el.parentNode); | |
| } | |
| } | |
| function toggleDetails(event) { | |
| // more sigh - need to check the clicked object | |
| var keypress = event.type == 'keypress', | |
| target = event.target || event.srcElement; | |
| if (keypress || isSummary(target)) { | |
| if (keypress) { | |
| // if it's a keypress, make sure it was enter or space | |
| keypress = event.which || event.keyCode; | |
| if (keypress == 32 || keypress == 13) { | |
| // all's good, go ahead and toggle | |
| } else { | |
| return; | |
| } | |
| } | |
| var open = this.getAttribute('open'); | |
| if (open === null) { | |
| this.setAttribute('open', 'open'); | |
| } else { | |
| this.removeAttribute('open'); | |
| } | |
| // this.className = open ? 'open' : ''; // Lame | |
| // trigger reflow (required in IE - sometimes in Safari too) | |
| setTimeout(function () { | |
| document.body.className = document.body.className; | |
| }, 13); | |
| if (keypress) { | |
| event.preventDefault && event.preventDefault(); | |
| return false; | |
| } | |
| } | |
| } | |
| function addStyle() { | |
| var style = document.createElement('style'), | |
| head = document.getElementsByTagName('head')[0], | |
| key = style.innerText === undefined ? 'textContent' : 'innerText'; | |
| var rules = ['details{display: block;}','details > *{display: none;}','details.open > *{display: block;}','details[open] > *{display: block;}','details > summary:first-child{display: block;cursor: pointer;}','details[open]{display: block;}']; | |
| i = rules.length; | |
| style[key] = rules.join("\n"); | |
| head.insertBefore(style, head.firstChild); | |
| } | |
| var details = document.getElementsByTagName('details'), | |
| wrapper, | |
| i = details.length, | |
| j, | |
| first = null, | |
| label = document.createElement('summary'); | |
| label.appendChild(document.createTextNode('Details')); | |
| while (i--) { | |
| first = firstNode(details[i]); | |
| if (first != null && first.nodeName.toUpperCase() == 'SUMMARY') { | |
| // we've found that there's a details label already | |
| } else { | |
| // first = label.cloneNode(true); // cloned nodes weren't picking up styles in IE - random | |
| first = document.createElement('summary'); | |
| first.appendChild(document.createTextNode('Details')); | |
| if (details[i].firstChild) { | |
| details[i].insertBefore(first, details[i].firstChild); | |
| } else { | |
| details[i].appendChild(first); | |
| } | |
| } | |
| // this feels *really* nasty, but we can't target details :text in css :( | |
| j = details[i].childNodes.length; | |
| while (j--) { | |
| if (details[i].childNodes[j].nodeName === '#text' && (details[i].childNodes[j].nodeValue||'').replace(/\s/g, '').length) { | |
| wrapper = document.createElement('text'); | |
| wrapper.appendChild(details[i].childNodes[j]); | |
| details[i].insertBefore(wrapper, details[i].childNodes[j]); | |
| } | |
| } | |
| first.legend = true; | |
| first.tabIndex = 0; | |
| } | |
| // trigger details in case this being used on it's own | |
| document.createElement('details'); | |
| addEvent(details, 'click', toggleDetails); | |
| addEvent(details, 'keypress', toggleDetails); | |
| addStyle(); | |
| })(window, document); |
|
Hmm - it is designed to... |
mathiasbynens
commented
Jul 4, 2010
|
(Remember this discussion?) |
|
Fixed....though I think I need a shower now :-S |
mathiasbynens
commented
Jul 6, 2010
|
I like the idea of wrapping direct child text nodes in a BTW, I completely agree with your comment on line132 :) I’ve updated the demo page for your snippet: http://jsbin.com/akadi4/2 IE8 and below seems to throw an error though (this happened before the update as well). |
codepo8
commented
Sep 7, 2010
|
Urgh testing for nodeName #text instead of nodeType? |
Yaffle
commented
Nov 15, 2011
|
WTF? |
|
@Yaffle WTF WTF? Why WTF? |
Yaffle
commented
Nov 15, 2011
|
i think it's better to use event delegation, so dynamically inserted will be supported too, also you will be able place script at any place in document(not only before )
|
|
@Yaffle ... so fork the script right, instead of getting all upset. Make it better - then we'll use your script and that's better for everyone. |
Yaffle
commented
Nov 15, 2011
|
@remy, ok, i will try a little later.
|
uniqname
commented
Jan 25, 2012
|
Wow. Awesome! I came up with a very similar approach to this and was looking for a solution to the text node problem and BAM! Remy beat me to it (by like year and a half!). It's nice to be vindicated in my approach though. Thanks for that :) |
francisrupert
commented
Jun 27, 2012
|
Rock. |
kapitancho
commented
Jul 6, 2012
|
Yaffle, you can try this script (supports dynamic inclusion): https://github.com/kapitancho/logifill-details |
chris-morgan
commented
Feb 10, 2014
•
|
I implemented |
kgmstwo
commented
Nov 22, 2014
|
I added a check so that it will work for nested details tags in https://gist.github.com/kgmstwo/53e189d87b41ac7743ee. |
jlgrall
commented
Aug 22, 2016
|
Doesn't work if there is a You may also get in trouble if in your |
mathiasbynens commentedJul 4, 2010
It should probably be noted that this solution doesn’t work with
<details>elements that contain only text (an edge case, admittedly).