1 |
2
|
Manuela
|
// Native Javascript for Bootstrap 3 | Collapse
|
2 |
|
|
// by dnp_theme
|
3 |
|
|
|
4 |
|
|
(function(factory){
|
5 |
|
|
|
6 |
|
|
// CommonJS/RequireJS and "native" compatibility
|
7 |
|
|
if(typeof module !== "undefined" && typeof exports == "object") {
|
8 |
|
|
// A commonJS/RequireJS environment
|
9 |
|
|
if(typeof window != "undefined") {
|
10 |
|
|
// Window and document exist, so return the factory's return value.
|
11 |
|
|
module.exports = factory();
|
12 |
|
|
} else {
|
13 |
|
|
// Let the user give the factory a Window and Document.
|
14 |
|
|
module.exports = factory;
|
15 |
|
|
}
|
16 |
|
|
} else {
|
17 |
|
|
// Assume a traditional browser.
|
18 |
|
|
window.Collapse = factory();
|
19 |
|
|
}
|
20 |
|
|
|
21 |
|
|
})(function(){
|
22 |
|
|
|
23 |
|
|
// COLLAPSE DEFINITION
|
24 |
|
|
// ===================
|
25 |
|
|
var Collapse = function( element, options ) {
|
26 |
|
|
options = options || {};
|
27 |
|
|
this.isIE = (new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null) ? parseFloat( RegExp.$1 ) : false;
|
28 |
|
|
this.btn = typeof element === 'object' ? element : document.querySelector(element);
|
29 |
|
|
this.accordion = null;
|
30 |
|
|
this.collapse = null;
|
31 |
|
|
this.duration = 300; // default collapse transition duration
|
32 |
|
|
this.options = {};
|
33 |
|
|
this.options.duration = (this.isIE && this.isIE < 10) ? 0 : (options.duration || this.duration);
|
34 |
|
|
this.init();
|
35 |
|
|
};
|
36 |
|
|
|
37 |
|
|
// COLLAPSE METHODS
|
38 |
|
|
// ================
|
39 |
|
|
Collapse.prototype = {
|
40 |
|
|
|
41 |
|
|
init : function() {
|
42 |
|
|
this.actions();
|
43 |
|
|
this.addEvent();
|
44 |
|
|
},
|
45 |
|
|
|
46 |
|
|
actions : function() {
|
47 |
|
|
var self = this;
|
48 |
|
|
var getOuterHeight = function (el) {
|
49 |
|
|
var s = el && el.currentStyle || window.getComputedStyle(el), // the getComputedStyle polyfill would do this for us, but we want to make sure it does
|
50 |
|
|
btp = s.borderTopWidth || 0,
|
51 |
|
|
mtp = /px/.test(s.marginTop) ? Math.round(s.marginTop.replace('px','')) : 0,
|
52 |
|
|
mbp = /px/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('px','')) : 0,
|
53 |
|
|
mte = /em/.test(s.marginTop) ? Math.round(s.marginTop.replace('em','') * parseInt(s.fontSize)) : 0,
|
54 |
|
|
mbe = /em/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('em','') * parseInt(s.fontSize)) : 0;
|
55 |
|
|
|
56 |
|
|
return el.clientHeight + parseInt( btp ) + parseInt( mtp ) + parseInt( mbp ) + parseInt( mte ) + parseInt( mbe ); //we need an accurate margin value
|
57 |
|
|
};
|
58 |
|
|
|
59 |
|
|
this.toggle = function(e) {
|
60 |
|
|
self.btn = self.getTarget(e).btn;
|
61 |
|
|
self.collapse = self.getTarget(e).collapse;
|
62 |
|
|
|
63 |
|
|
if (!/\bin/.test(self.collapse.className)) {
|
64 |
|
|
self.open(e);
|
65 |
|
|
} else {
|
66 |
|
|
self.close(e);
|
67 |
|
|
}
|
68 |
|
|
},
|
69 |
|
|
this.close = function(e) {
|
70 |
|
|
e.preventDefault();
|
71 |
|
|
self.btn = self.getTarget(e).btn;
|
72 |
|
|
self.collapse = self.getTarget(e).collapse;
|
73 |
|
|
self._close(self.collapse);
|
74 |
|
|
self.removeClass(self.btn,'collapsed');
|
75 |
|
|
},
|
76 |
|
|
this.open = function(e) {
|
77 |
|
|
e.preventDefault();
|
78 |
|
|
self.btn = self.getTarget(e).btn;
|
79 |
|
|
self.collapse = self.getTarget(e).collapse;
|
80 |
|
|
self.accordion = self.btn.getAttribute('data-parent') && self.getClosest(self.btn, self.btn.getAttribute('data-parent'));
|
81 |
|
|
|
82 |
|
|
self._open(self.collapse);
|
83 |
|
|
self.addClass(self.btn,'collapsed');
|
84 |
|
|
|
85 |
|
|
if ( self.accordion !== null ) {
|
86 |
|
|
var active = self.accordion.querySelectorAll('.collapse.in'), al = active.length, i = 0;
|
87 |
|
|
for (i;i<al;i++) {
|
88 |
|
|
if ( active[i] !== self.collapse) self._close(active[i]);
|
89 |
|
|
}
|
90 |
|
|
}
|
91 |
|
|
},
|
92 |
|
|
this._open = function(c) {
|
93 |
|
|
self.removeEvent();
|
94 |
|
|
self.addClass(c,'in');
|
95 |
|
|
c.setAttribute('aria-expanded','true');
|
96 |
|
|
self.addClass(c,'collapsing');
|
97 |
|
|
setTimeout(function() {
|
98 |
|
|
var h = self.getMaxHeight(c);
|
99 |
|
|
c.style.height = h + 'px';
|
100 |
|
|
c.style.overflowY = 'hidden';
|
101 |
|
|
}, 0);
|
102 |
|
|
setTimeout(function() {
|
103 |
|
|
c.style.height = '';
|
104 |
|
|
c.style.overflowY = '';
|
105 |
|
|
self.removeClass(c,'collapsing');
|
106 |
|
|
self.addEvent();
|
107 |
|
|
}, self.options.duration);
|
108 |
|
|
},
|
109 |
|
|
this._close = function(c) {
|
110 |
|
|
self.removeEvent();
|
111 |
|
|
c.setAttribute('aria-expanded','false');
|
112 |
|
|
c.style.height = self.getMaxHeight(c) + 'px';
|
113 |
|
|
setTimeout(function() {
|
114 |
|
|
c.style.height = '0px';
|
115 |
|
|
c.style.overflowY = 'hidden';
|
116 |
|
|
self.addClass(c,'collapsing');
|
117 |
|
|
}, 0);
|
118 |
|
|
|
119 |
|
|
setTimeout(function() {
|
120 |
|
|
self.removeClass(c,'collapsing');
|
121 |
|
|
self.removeClass(c,'in');
|
122 |
|
|
c.style.overflowY = '';
|
123 |
|
|
c.style.height = '';
|
124 |
|
|
self.addEvent();
|
125 |
|
|
}, self.options.duration);
|
126 |
|
|
},
|
127 |
|
|
this.getMaxHeight = function(l) { // get collapse trueHeight and border
|
128 |
|
|
var h = 0;
|
129 |
|
|
for (var k = 0, ll = l.children.length; k < ll; k++) {
|
130 |
|
|
h += getOuterHeight(l.children[k]);
|
131 |
|
|
}
|
132 |
|
|
return h;
|
133 |
|
|
},
|
134 |
|
|
this.removeEvent = function() {
|
135 |
|
|
this.btn.removeEventListener('click', this.toggle, false);
|
136 |
|
|
},
|
137 |
|
|
this.addEvent = function() {
|
138 |
|
|
this.btn.addEventListener('click', this.toggle, false);
|
139 |
|
|
},
|
140 |
|
|
this.getTarget = function(e) {
|
141 |
|
|
var t = e.currentTarget || e.srcElement,
|
142 |
|
|
h = t.href && t.getAttribute('href').replace('#',''),
|
143 |
|
|
d = t.getAttribute('data-target') && ( t.getAttribute('data-target') ),
|
144 |
|
|
id = h || ( d && /#/.test(d)) && d.replace('#',''),
|
145 |
|
|
cl = (d && d.charAt(0) === '.') && d, //the navbar collapse trigger targets a class
|
146 |
|
|
c = id && document.getElementById(id) || cl && document.querySelector(cl);
|
147 |
|
|
|
148 |
|
|
return {
|
149 |
|
|
btn : t,
|
150 |
|
|
collapse : c
|
151 |
|
|
};
|
152 |
|
|
},
|
153 |
|
|
|
154 |
|
|
this.getClosest = function (el, s) { //el is the element and s the selector of the closest item to find
|
155 |
|
|
// source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/
|
156 |
|
|
var f = s.charAt(0);
|
157 |
|
|
for ( ; el && el !== document; el = el.parentNode ) {// Get closest match
|
158 |
|
|
if ( f === '.' ) {// If selector is a class
|
159 |
|
|
if ( document.querySelector(s) !== undefined ) { return el; }
|
160 |
|
|
}
|
161 |
|
|
if ( f === '#' ) { // If selector is an ID
|
162 |
|
|
if ( el.id === s.substr(1) ) { return el; }
|
163 |
|
|
}
|
164 |
|
|
}
|
165 |
|
|
return false;
|
166 |
|
|
};
|
167 |
|
|
this.addClass = function(el,c) {
|
168 |
|
|
if (el.classList) { el.classList.add(c); } else { el.className += ' '+c; }
|
169 |
|
|
};
|
170 |
|
|
this.removeClass = function(el,c) {
|
171 |
|
|
if (el.classList) { el.classList.remove(c); } else { el.className = el.className.replace(c,'').replace(/^\s+|\s+$/g,''); }
|
172 |
|
|
};
|
173 |
|
|
}
|
174 |
|
|
};
|
175 |
|
|
|
176 |
|
|
// COLLAPSE DATA API
|
177 |
|
|
// =================
|
178 |
|
|
var Collapses = document.querySelectorAll('[data-toggle="collapse"]'), i = 0, cll = Collapses.length;
|
179 |
|
|
for (i;i<cll;i++) {
|
180 |
|
|
var item = Collapses[i], options = {};
|
181 |
|
|
options.duration = item.getAttribute('data-duration');
|
182 |
|
|
new Collapse(item,options);
|
183 |
|
|
}
|
184 |
|
|
|
185 |
|
|
return Collapse;
|
186 |
|
|
|
187 |
|
|
});
|