-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathdetails-shim.js
137 lines (125 loc) · 4.66 KB
/
details-shim.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* details-shim.js
* A pure JavaScript (no dependencies) solution to make HTML5
* Details/Summary tags work in unsupportive browsers
*
* Copyright (c) 2013 Tyler Uebele
* Released under the MIT license. See included LICENSE.txt
* or http://opensource.org/licenses/MIT
*
* latest version available at https://github.com/tyleruebele/details-shim
*/
/**
* Enable proper operation of <details> tags in unsupportive browsers
*
* @param Details details element to shim
* @returns {boolean} false on error
*/
function details_shim(Details) {
// For backward compatibility, if no DOM Element is sent, call init()
if (!Details || !('nodeType' in Details) || !('tagName' in Details)) {
return details_shim.init();
}
var Summary;
// If we were passed a details tag, find its summary tag
if ('details' == Details.tagName.toLowerCase()) {
// Assume first found summary tag is the corresponding summary tag
Summary = Details.getElementsByTagName('summary')[0];
// If we were passed a summary tag, find its details tag
} else if (!!Details.parentNode
&& 'summary' == Details.tagName.toLowerCase()
) {
Summary = Details;
Details = Summary.parentNode;
} else {
// An invalid parameter was passed for Details
return false;
}
// If the details tag is natively supported or already shimmed
if ('boolean' == typeof Details.open) {
// If native, remove custom classes
if (!Details.getAttribute('data-open')) {
Details.className = Details.className
.replace(/\bdetails_shim_open\b|\bdetails_shim_closed\b/g, ' ');
}
return false;
}
// Set initial class according to `open` attribute
var state = Details.outerHTML
// OR older firefox doesn't have .outerHTML
|| new XMLSerializer().serializeToString(Details);
state = state.substring(0, state.indexOf('>'));
// Read: There is an open attribute, and it's not explicitly empty
state = (-1 != state.indexOf('open') && -1 == state.indexOf('open=""'))
? 'open'
: 'closed'
;
Details.setAttribute('data-open', state);
Details.className += ' details_shim_' + state;
// Add onclick handler to toggle visibility class
Summary.addEventListener
? Summary.addEventListener('click', function() { details_shim.toggle(Details); })
: Summary.attachEvent && Summary.attachEvent('onclick', function() { details_shim.toggle(Details); })
;
Object.defineProperty(Details, 'open', {
get: function() {
return 'open' == this.getAttribute('data-open');
},
set: function(state) {
details_shim.toggle(this, state);
}
});
// wrap text nodes in span to expose them to css
for (var j = 0; j < Details.childNodes.length; j++) {
if (Details.childNodes[j].nodeType == 3
&& /[^\s]/.test(Details.childNodes[j].data)
) {
var span = document.createElement('span');
var text = Details.childNodes[j];
Details.insertBefore(span, text);
Details.removeChild(text);
span.appendChild(text);
}
}
} // details_shim()
/**
* Toggle the open state of specified <details> tag
* @param Details The <details> tag to toggle
* @param state Optional override state
*/
details_shim.toggle = function(Details, state) {
// If state was not passed, seek current state
if ('undefined' === typeof state) {
// new state
state = Details.getAttribute('data-open') == 'open'
? 'closed'
: 'open'
;
} else {
// Sanitize the input, expect boolean, force string
// Expecting boolean means even 'closed' will result in an open
// This is the behavior of the natively supportive browsers
state = !!state ? 'open' : 'closed';
}
Details.setAttribute('data-open', state);
// replace previous open/close class
Details.className = Details.className
.replace(/\bdetails_shim_open\b|\bdetails_shim_closed\b/g, ' ')
+ ' details_shim_' + state;
};
/**
* Run details_shim() on each details tag
*/
details_shim.init = function() {
// Because <details> must include a <summary>,
// collecting <summary> tags collects *valid* <details> tags
var Summaries = document.getElementsByTagName('summary');
for (var i = 0; i < Summaries.length; i++) {
details_shim(Summaries[i]);
}
};
// Run details_shim.init() when the page loads
window.addEventListener
? window.addEventListener('load', details_shim.init, false)
: window.attachEvent && window.attachEvent('onload', details_shim.init)
;