HTML5 Details, Summary fallbakc w/ JS


/ Published in: JavaScript
Save to your folder(s)



Copy this code and paste it in your HTML
  1. /**
  2.  * Note that this script is intended to be included at the *end* of the document, before </body>
  3.  */
  4. (function (window, document) {
  5. if ('open' in document.createElement('details')) return;
  6.  
  7. // made global by myself to be reused elsewhere
  8. var addEvent = (function () {
  9. if (document.addEventListener) {
  10. return function (el, type, fn) {
  11. if (el && el.nodeName || el === window) {
  12. el.addEventListener(type, fn, false);
  13. } else if (el && el.length) {
  14. for (var i = 0; i < el.length; i++) {
  15. addEvent(el[i], type, fn);
  16. }
  17. }
  18. };
  19. } else {
  20. return function (el, type, fn) {
  21. if (el && el.nodeName || el === window) {
  22. el.attachEvent('on' + type, function () { return fn.call(el, window.event); });
  23. } else if (el && el.length) {
  24. for (var i = 0; i < el.length; i++) {
  25. addEvent(el[i], type, fn);
  26. }
  27. }
  28. };
  29. }
  30. })();
  31.  
  32.  
  33. /** details support - typically in it's own script */
  34. // find the first /real/ node
  35. function firstNode(source) {
  36. var node = null;
  37. if (source.firstChild.nodeName != "#text") {
  38. return source.firstChild;
  39. } else {
  40. source = source.firstChild;
  41. do {
  42. source = source.nextSibling;
  43. } while (source && source.nodeName == '#text');
  44.  
  45. return source || null;
  46. }
  47. }
  48.  
  49. function isSummary(el) {
  50. var nn = el.nodeName.toUpperCase();
  51. if (nn == 'DETAILS') {
  52. return false;
  53. } else if (nn == 'SUMMARY') {
  54. return true;
  55. } else {
  56. return isSummary(el.parentNode);
  57. }
  58. }
  59.  
  60. function toggleDetails(event) {
  61. // more sigh - need to check the clicked object
  62. var keypress = event.type == 'keypress',
  63. target = event.target || event.srcElement;
  64. if (keypress || isSummary(target)) {
  65. if (keypress) {
  66. // if it's a keypress, make sure it was enter or space
  67. keypress = event.which || event.keyCode;
  68. if (keypress == 32 || keypress == 13) {
  69. // all's good, go ahead and toggle
  70. } else {
  71. return;
  72. }
  73. }
  74.  
  75. var open = this.getAttribute('open');
  76. if (open === null) {
  77. this.setAttribute('open', 'open');
  78. } else {
  79. this.removeAttribute('open');
  80. }
  81.  
  82. // this.className = open ? 'open' : ''; // Lame
  83. // trigger reflow (required in IE - sometimes in Safari too)
  84. setTimeout(function () {
  85. document.body.className = document.body.className;
  86. }, 13);
  87.  
  88. if (keypress) {
  89. event.preventDefault && event.preventDefault();
  90. return false;
  91. }
  92. }
  93. }
  94.  
  95. function addStyle() {
  96. var style = document.createElement('style'),
  97. head = document.getElementsByTagName('head')[0],
  98. key = style.innerText === undefined ? 'textContent' : 'innerText';
  99.  
  100. 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;}'];
  101. i = rules.length;
  102.  
  103. style[key] = rules.join("\n");
  104. head.insertBefore(style, head.firstChild);
  105. }
  106.  
  107. var details = document.getElementsByTagName('details'),
  108. wrapper,
  109. i = details.length,
  110. j,
  111. first = null,
  112. label = document.createElement('summary');
  113.  
  114. label.appendChild(document.createTextNode('Details'));
  115.  
  116. while (i--) {
  117. first = firstNode(details[i]);
  118.  
  119. if (first != null && first.nodeName.toUpperCase() == 'SUMMARY') {
  120. // we've found that there's a details label already
  121. } else {
  122. // first = label.cloneNode(true); // cloned nodes weren't picking up styles in IE - random
  123. first = document.createElement('summary');
  124. first.appendChild(document.createTextNode('Details'));
  125. if (details[i].firstChild) {
  126. details[i].insertBefore(first, details[i].firstChild);
  127. } else {
  128. details[i].appendChild(first);
  129. }
  130. }
  131.  
  132. // this feels *really* nasty, but we can't target details :text in css :(
  133. j = details[i].childNodes.length;
  134. while (j--) {
  135. if (details[i].childNodes[j].nodeName === '#text' && (details[i].childNodes[j].nodeValue||'').replace(/\s/g, '').length) {
  136. wrapper = document.createElement('text');
  137. wrapper.appendChild(details[i].childNodes[j]);
  138. details[i].insertBefore(wrapper, details[i].childNodes[j]);
  139. }
  140. }
  141.  
  142. first.legend = true;
  143. first.tabIndex = 0;
  144. }
  145.  
  146. // trigger details in case this being used on it's own
  147. document.createElement('details');
  148. addEvent(details, 'click', toggleDetails);
  149. addEvent(details, 'keypress', toggleDetails);
  150. addStyle();
  151.  
  152. })(window, document);

URL: https://gist.github.com/370590

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.