Skip to content

Commit

Permalink
Way better implimentation
Browse files Browse the repository at this point in the history
This version includes fixes for dynamically added content as well as
unwrapped text nodes. Yay!
  • Loading branch information
uniqname committed Jan 30, 2012
1 parent 228cf89 commit bd48c5c
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 37 deletions.
81 changes: 51 additions & 30 deletions details.polyfill.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
/* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php */
(function (doc) {
'use strict';
var i,
var i, j,
textWrapper,
idCount = 0,
detailsID,
detailsElem,
summaryElem,
height, //height of the summary element
rootNode = doc.documentElement,
headElem = doc.getElementsByTagName('head')[0] || rootNode,
bodyElem = doc.getElementsByTagName('body')[0] || rootNode,
detailsElems = doc.getElementsByTagName('details'),
detailStyleTag = doc.createElement('style'),
rules = 'details { display: block; } \n' +
rules = 'details { display: block; overflow:hidden; } \n' +
'details[open] { height: auto; } \n' +
'details > summary:first-child { visibility: visible; cursor: pointer; display:block; margin-bottom: 9999em; } \n' +
'details > summary:first-child { visibility: visible; cursor: pointer; display:inline-block; } \n' +
'details > * { visibility: hidden; } \n' +
'details[open] > * { visibility: visible } \n' +
'details[open] > summary:first-child { margin-bottom: 0; display: inline; }',
/* Technically, a summary element has a "Phrasing content" model and should be displayed inline.
* see http://dev.w3.org/html5/spec/Overview.html#the-summary-element,
Expand Down Expand Up @@ -52,31 +50,54 @@
} else { detailsElmnt.setAttribute('open', 'open'); }
}
}
},
init = function () {
var detailsID,
detailsElem,
summaryElem,
height, //height of the summary element
detailsElems = doc.getElementsByTagName('details');

for (i = 0; i < detailsElems.length; i++) {
detailsElem = detailsElems[i];
if (!detailsElem.getAttribute('data-detailsid')) {
detailsID = 'd' + (idCount++);
detailsElem.setAttribute('data-detailsID', detailsID);
/* The spec expects the functional <summary> element to be the first child node of a
* <details> element. In practice, it appears the first child <summary> element of a
* <detials> element is enlisted as the functional <summary> element and displayed as
* though it were the first child. For our purposes, we will do that explicitly.
* Additionaly, If a <summary> element does not exist, a default toggle is provided.
*/
summaryElem = detailsElem.getElementsByTagName('summary')[0];
if (!summaryElem) {
summaryElem = doc.createElement('summary');
summaryElem.appendChild(doc.createTextNode('Details'));
detailsElem.insertBefore(summaryElem, detailsElem.firstChild);
} else if ( summaryElem !== detailsElem.firstChild) {
detailsElem.removeChild(summaryElem);
detailsElem.insertBefore(summaryElem, detailsElem.firstChild);
}
height = summaryElem.offsetHeight;
addRule(detailStyleTag, 'details[data-detailsid="' + detailsID + '"] { height: ' + height + 'px; }\n' +
'details[data-detailsid="' + detailsID + '"][open] { height: auto; }');

//Text nodes are killing me here. Thanks to @Remy for the solve.
for (j = 0; j < detailsElem.childNodes.length; j++ ) {
if (detailsElem.childNodes[j].nodeName === '#text' && (detailsElem.childNodes[j].nodeValue||'').replace(/\s/g, '').length) {
textWrapper = document.createElement('text');
textWrapper.appendChild(detailsElem.childNodes[j]);
detailsElem.insertBefore(textWrapper, detailsElem.childNodes[j]);
}
}
}
}
};

init();
document.getElementsByTagName('body')[0].addEventListener('DOMSubtreeModified', init, false);
addRule(detailStyleTag, rules);
for (i = 0; i < detailsElems.length; i++) {
detailsElem = detailsElems[i];
detailsID = 'd' + (idCount++);
detailsElem.setAttribute('data-detailsID', detailsID);
/* The spec expects the functional <summary> element to be the first child node of a <details> element.
* In practice, it appears the first child <summary> element of a <detials> element is enlisted as the
* functional <summary> element and displayed as though it were the first child. For our purposes, we will
* do that explicitly. Additionaly, If a <summary> element does not exist, a default toggle is provided.
*/
summaryElem = detailsElem.getElementsByTagName('summary')[0];
if (!summaryElem) {
summaryElem = doc.createElement('summary');
summaryElem.appendChild(doc.createTextNode('Details'));
detailsElem.insertBefore(summaryElem, detailsElem.firstChild);
} else if ( summaryElem !== detailsElem.firstChild) {
detailsElem.removeChild(summaryElem);
detailsElem.insertBefore(summaryElem, detailsElem.firstChild);
}
height = summaryElem.offsetHeight;
addRule(detailStyleTag, 'details[data-detailsid="' + detailsID + '"] { height: ' + height + 'px; }\n' +
'details[data-detailsid="' + detailsID + '"][open] { height: auto; }');
}

/* The inserted stylesheet needs to be first so as to have a minimal cascading coeffecient.
* The polyfill only adds default styling and should not interfere with other style rules.
*/
Expand Down
2 changes: 1 addition & 1 deletion details.polyfill.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 13 additions & 6 deletions example.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<style>
.example {
details, .example {
padding: 2em;
background: #fafafa;
border: 1px solid #eee;
Expand All @@ -26,6 +26,7 @@
p { color: #333;}

</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>
/*yepnope1.0.2|WTFPL*/
(function(a,b,c){function H(){var a=z;a.loader={load:G,i:0};return a}function G(a,b,c){var e=b=="c"?r:q;i=0,b=b||"j",u(a)?F(e,a,b,this.i++,d,c):(h.splice(this.i++,0,a),h.length==1&&E());return this}function F(a,c,d,g,j,l){function q(){!o&&A(n.readyState)&&(p.r=o=1,!i&&B(),n.onload=n.onreadystatechange=null,e(function(){m.removeChild(n)},0))}var n=b.createElement(a),o=0,p={t:d,s:c,e:l};n.src=n.data=c,!k&&(n.style.display="none"),n.width=n.height="0",a!="object"&&(n.type=d),n.onload=n.onreadystatechange=q,a=="img"?n.onerror=q:a=="script"&&(n.onerror=function(){p.e=p.r=1,E()}),h.splice(g,0,p),m.insertBefore(n,k?null:f),e(function(){o||(m.removeChild(n),p.r=p.e=o=1,B())},z.errorTimeout)}function E(){var a=h.shift();i=1,a?a.t?e(function(){a.t=="c"?D(a):C(a)},0):(a(),B()):i=0}function D(a){var c=b.createElement("link"),d;c.href=a.s,c.rel="stylesheet",c.type="text/css";if(!a.e&&(o||j)){var g=function(a){e(function(){if(!d)try{a.sheet.cssRules.length?(d=1,B()):g(a)}catch(b){b.code==1e3||b.message=="security"||b.message=="denied"?(d=1,e(function(){B()},0)):g(a)}},0)};g(c)}else c.onload=function(){d||(d=1,e(function(){B()},0))},a.e&&c.onload();e(function(){d||(d=1,B())},z.errorTimeout),!a.e&&f.parentNode.insertBefore(c,f)}function C(a){var c=b.createElement("script"),d;c.src=a.s,c.onreadystatechange=c.onload=function(){!d&&A(c.readyState)&&(d=1,B(),c.onload=c.onreadystatechange=null)},e(function(){d||(d=1,B())},z.errorTimeout),a.e?c.onload():f.parentNode.insertBefore(c,f)}function B(){var a=1,b=-1;while(h.length- ++b)if(h[b].s&&!(a=h[b].r))break;a&&E()}function A(a){return!a||a=="loaded"||a=="complete"}var d=b.documentElement,e=a.setTimeout,f=b.getElementsByTagName("script")[0],g={}.toString,h=[],i=0,j="MozAppearance"in d.style,k=j&&!!b.createRange().compareNode,l=j&&!k,m=k?d:f.parentNode,n=a.opera&&g.call(a.opera)=="[object Opera]",o="webkitAppearance"in d.style,p=o&&"async"in b.createElement("script"),q=j?"object":n||p?"img":"script",r=o?"img":q,s=Array.isArray||function(a){return g.call(a)=="[object Array]"},t=function(a){return Object(a)===a},u=function(a){return typeof a=="string"},v=function(a){return g.call(a)=="[object Function]"},w=[],x={},y,z;z=function(a){function h(a,b){function i(a){if(u(a))g(a,f,b,0,c);else if(t(a))for(h in a)a.hasOwnProperty(h)&&g(a[h],f,b,h,c)}var c=!!a.test,d=c?a.yep:a.nope,e=a.load||a.both,f=a.callback,h;i(d),i(e),a.complete&&b.load(a.complete)}function g(a,b,d,e,g){var h=f(a),i=h.autoCallback;if(!h.bypass){b&&(b=v(b)?b:b[a]||b[e]||b[a.split("/").pop().split("?")[0]]);if(h.instead)return h.instead(a,b,d,e,g);d.load(h.url,h.forceCSS||!h.forceJS&&/css$/.test(h.url)?"c":c,h.noexec),(v(b)||v(i))&&d.load(function(){H(),b&&b(h.origUrl,g,e),i&&i(h.origUrl,g,e)})}}function f(a){var b=a.split("!"),c=w.length,d=b.pop(),e=b.length,f={url:d,origUrl:d,prefixes:b},g,h;for(h=0;h<e;h++)g=x[b[h]],g&&(f=g(f));for(h=0;h<c;h++)f=w[h](f);return f}var b,d,e=this.yepnope.loader;if(u(a))g(a,0,e,0);else if(s(a))for(b=0;b<a.length;b++)d=a[b],u(d)?g(d,0,e,0):s(d)?z(d):t(d)&&h(d,e);else t(a)&&h(a,e)},z.addPrefix=function(a,b){x[a]=b},z.addFilter=function(a){w.push(a)},z.errorTimeout=1e4,b.readyState==null&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",y=function(){b.removeEventListener("DOMContentLoaded",y,0),b.readyState="complete"},0)),a.yepnope=H()})(this,this.document)
Expand All @@ -37,36 +38,42 @@
};
yepnope({
test: Supports.details,
nope: 'details.polyfill.js'
nope: 'details.polyfill.min.js'
});
</script>
</head>
<body>
<details class="example">
<details>
<summary>And the winner is...</summary>
<summary>Drumroll please...</summary>
<summary>The anticipation is killing me...</summary>
Me of course.
</details>


<details class="example">
<details>
And the winner is...
<p>Drumroll please...</p>
<summary>The anticipation is killing me...</summary>
Me of course.
</details>
<div class="example">
<div>
<summary>And the winner is...</summary>
<details>
<p>Drumroll please...</p>
<p>The anticipation is killing me...</p>
Me of course.
</details>
</div>
<details class="example">
<details>
<summary>And the winner is...</summary> Drumroll please... </span>The anticipation is killing me...</span>
Me of course.
</details>
<script>
var append = function () {
$('body').append('<details><summary>Hey, hey. You, you.</summary> I dont like your girlfriend</details>');
}
setTimeout(append, 4000);
</script>
</body>
</html>

0 comments on commit bd48c5c

Please sign in to comment.