Skip to content

Commit

Permalink
* fixed Collapse overflow and other code improvements
Browse files Browse the repository at this point in the history
* changed Button's `change` event to `bs.button.change`, it works better than the native `change`
* minor Carousel improvements, if no `CustomEvent` found, script should still work without `element does not support that property` error
* minor change to Scrollspy
  • Loading branch information
thednp committed Oct 15, 2015
1 parent b4fc70e commit 7600c97
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 187 deletions.
21 changes: 10 additions & 11 deletions assets/js/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,23 @@ mainSlider.addEventListener('slide.bs.carousel', function(e) {
});

//demo for Button toggle
var radioBtns = document.querySelectorAll('[name="options"]'); // radios
var checkBtns = document.querySelectorAll('input[type="checkbox"]'); // checkboxes
var radioBtns = document.querySelectorAll('input[type="radio"]'); // radios

document.querySelectorAll('[data-toggle="buttons"]')[0].addEventListener('change', function(e) {
console.log( 'The button group with checkboxes inside changed, and you see this 3 times because there are 3 buttons inside the group' );
document.getElementById('checkboxButtons').addEventListener('bs.button.change', function() { // checkboxes group
console.log( 'The button group with CHECKBOXES changed' );
});
document.querySelectorAll('[data-toggle="buttons"]')[1].addEventListener('change', function(e) {
console.log( 'The button group with radios inside changed' );
checkBtns[0].addEventListener('bs.button.change',function() {
console.log( 'Hopa! This handler is bound by the "change" of the FIRST CHECKBOX only.' );
});

radioBtns[0].addEventListener('change', function(e) {
console.log( 'This handler is bound by the "change" of the first radio button only.' );
document.getElementById('radioButtons').addEventListener('bs.button.change', function() { // radios group
console.log( 'The button group with RADIOS changed' );
});

checkBtns[0].addEventListener('change',function() {
console.log( 'Hopa! The first checkbox changed! This handler is bound by the "change" of the first checkbox only.' );
})

radioBtns[0].addEventListener('bs.button.change', function() {
console.log( 'This handler is bound by the "change" of the FIRST RADIO button only.' );
});

/* side-nav autoresize on window scroll and resize */
if ( document.documentElement && !/ie/.test(document.documentElement.className) ) {
Expand Down
141 changes: 66 additions & 75 deletions dist/bootstrap-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,9 @@
},

actions : function() {
var self = this, isIE = /ie/.test(document.documentElement.className),
changeEvent = (('Event' in window) && window.dispatchEvent) ? new Event('change') : null; // The native event that will be triggered on demand for IE
var self = this,
changeEvent = (('CustomEvent' in window) && window.dispatchEvent)
? new CustomEvent('bs.button.change') : null; // The custom event that will be triggered on demand

// assign event to a trigger function
function triggerChange(t) { if (changeEvent) { t.dispatchEvent(changeEvent); } }
Expand Down Expand Up @@ -336,8 +337,8 @@
input.getAttribute('checked');
input.removeAttribute('checked');
}
if (isIE) { triggerChange(input); } //trigger the change for the input
if (isIE) { triggerChange(self.btn); } //trigger the change for the btn-group
triggerChange(input); //trigger the change for the input
triggerChange(self.btn); //trigger the change for the btn-group
}

if ( input.type === 'radio' ) { // radio buttons
Expand All @@ -346,7 +347,7 @@
input.setAttribute('checked','checked');

triggerChange(self.btn);
if (isIE) { triggerChange(input); } //trigger the change
triggerChange(input); //trigger the change

for (i;i<ll;i++) {
var l = labels[i];
Expand Down Expand Up @@ -494,7 +495,7 @@
var direction = self.direction;
var dr = direction === 'left' ? 'next' : 'prev';
var slid = null, slide=null;

//register events
if (('CustomEvent' in window) && window.dispatchEvent) {
slid = new CustomEvent("slid.bs.carousel");
Expand Down Expand Up @@ -717,16 +718,28 @@
// allows the collapse to expand
// ** when window gets resized
// ** or via internal clicks handers such as dropwowns or any other
document.addEventListener('click', this.update, false);
window.addEventListener('resize', this.update, false)
window.addEventListener('resize', this.update, false);
},

actions : function() {
var self = this;
var getOuterHeight = function (el) {
var s = el && el.currentStyle || window.getComputedStyle(el), // the getComputedStyle polyfill would do this for us, but we want to make sure it does
btp = s.borderTopWidth || 0,
mtp = /px/.test(s.marginTop) ? Math.round(s.marginTop.replace('px','')) : 0,
mbp = /px/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('px','')) : 0,
mte = /em/.test(s.marginTop) ? Math.round(s.marginTop.replace('em','') * parseInt(s.fontSize)) : 0,
mbe = /em/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('em','') * parseInt(s.fontSize)) : 0;

return el.clientHeight + parseInt( btp ) + parseInt( mtp ) + parseInt( mbp ) + parseInt( mte ) + parseInt( mbe ) //we need an accurate margin value
};

this.toggle = function(e) {
var tg = false;
self.btn = self.getTarget(e).btn;
self.collapse = self.getTarget(e).collapse;

if (!tg){self.collapse.addEventListener('click', self.update, false); tg = true;}

if (!/in/.test(self.collapse.className)) {
self.open(e)
Expand Down Expand Up @@ -757,54 +770,45 @@
}
}
},
this._open = function(c) {

c.className += ' in';
c.style.height = 0;
c.style.overflow = 'hidden';
this._open = function(c) {
c.className += ' in';
c.setAttribute('area-expanded','true');

// the collapse MUST have a childElement div to wrap them all inside, just like accordion/well
var oh = this.getMaxHeight(c).oh, br = this.getMaxHeight(c).br;

c.style.height = oh + br + 'px';
setTimeout(function() {
c.style.overflow = '';
}, self.options.duration)

c.style.height = '0px';
var ch = this.getMaxHeight(c);
this._resize(c,ch);
},
this._close = function(c) {

c.style.overflow = 'hidden';
c.style.height = 0;
c.setAttribute('area-expanded','false');
c.className += ' collapsing';
c.style.overflowY = 'hidden';
c.style.height = '0px';
setTimeout(function() {
c.className = c.className.replace(' in','');
c.style.overflow = '';
c.setAttribute('area-expanded','false');
c.style.overflowY = '';
c.className = c.className.replace(' in collapsing','');
}, self.options.duration)
},
this.update = function(e) {
var evt = e.type, tg = e.target, closest = self.getClosest(tg,'.collapse'),
itms = document.querySelectorAll('.collapse.in'), i = 0, il = itms.length;
for (i;i<il;i++) {
var itm = itms[i], oh = self.getMaxHeight(itm).oh, br = self.getMaxHeight(itm).br;

if ( evt === 'resize' && !/ie/.test(document.documentElement.className) ){
setTimeout(function() {
itm.style.height = oh + br + 'px';
}, self.options.duration)
} else if ( evt === 'click' && closest === itm ) {
itm.style.height = oh + br + 'px';
}
var evt = e.type, itms = document.querySelectorAll('.collapse.in'), i = 0, il = itms.length;
if ( evt === 'resize' && !/ie/.test(document.documentElement.className) ) {
for (i;i<il;i++) {
self._resize(itms[i],self.getMaxHeight(itms[i]))
}
} else if ( evt === 'click' ) {
self._resize(this,self.getMaxHeight(this))
}
},
this._resize = function(l,h) { // set new resize
l.className += ' collapsing';
l.style.overflowY = 'hidden';
l.style.height = h + 'px';
setTimeout(function() {
l.style.overflowY = '';
l.className = l.className.replace(' collapsing','');
}, self.options.duration+50)
},
this.getMaxHeight = function(l) { // get collapse trueHeight and border
var t = l.children[0];
var cs = l.currentStyle || window.getComputedStyle(l);

return {
oh : getOuterHeight(t),
br : parseInt(cs.borderTop||0) + parseInt(cs.borderBottom||0)
}
return getOuterHeight(l.children[0]);
},
this.getTarget = function(e) {
var t = e.currentTarget || e.srcElement,
Expand All @@ -824,30 +828,30 @@
// source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/
var f = s.charAt(0);
for ( ; el && el !== document; el = el.parentNode ) {// Get closest match

if ( f === '.' ) {// If selector is a class
if ( document.querySelector(s) !== undefined ) { return el; }
}

if ( f === '#' ) { // If selector is an ID
if ( el.id === s.substr(1) ) { return el; }
}
}
return false;
}
};

//we must add the height to the pre-opened collapses
window.addEventListener('load', function() {
var openedCollapses = document.querySelectorAll('.collapse'), i = 0, ocl = openedCollapses.length;
for (i;i<ocl;i++) {
var oc = openedCollapses[i];
if (oc && /in/.test(oc.className)) {
var ch = getOuterHeight(oc.children[0]);
oc.style.height = ch + 'px';
}
}
});
}
}

var getOuterHeight = function (el) {
var s = el && el.currentStyle || window.getComputedStyle(el),
mtp = /px/.test(s.marginTop) ? Math.round(s.marginTop.replace('px','')) : 0,
mbp = /px/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('px','')) : 0,
mte = /em/.test(s.marginTop) ? Math.round(s.marginTop.replace('em','') * parseInt(s.fontSize)) : 0,
mbe = /em/.test(s.marginBottom) ? Math.round(s.marginBottom.replace('em','') * parseInt(s.fontSize)) : 0;

return el.offsetHeight + parseInt( mtp ) + parseInt( mbp ) + parseInt( mte ) + parseInt( mbe ) //we need an accurate margin value
}

// COLLAPSE DATA API
// =================
var Collapses = document.querySelectorAll('[data-toggle="collapse"]'), i = 0, cll = Collapses.length;
Expand All @@ -857,20 +861,6 @@
new Collapse(item,options);
}

//we must add the height to the pre-opened collapses
window.addEventListener('load', function() {
var openedCollapses = document.querySelectorAll('.collapse'), i = 0, ocl = openedCollapses.length;
for (i;i<ocl;i++) {
var oc = openedCollapses[i];
if (/in/.test(oc.className)) {
var s = oc.currentStyle || window.getComputedStyle(oc);
var oh = getOuterHeight(oc.children[0]);
var br = parseInt(s.borderTop||0) + parseInt(s.borderBottom||0);
oc.style.height = oh + br + 'px';
}
}
});

return Collapse;

});
Expand Down Expand Up @@ -1477,14 +1467,14 @@
},
topLimit: function () { // the target offset
if ( this.scrollTarget === window ) {
return this.tg.getBoundingClientRect().top + this.scrollOffset()
return this.tg.getBoundingClientRect().top + this.scrollOffset() - 5
} else {
return this.tg.offsetTop;
}

},
bottomLimit: function () {
return this.topLimit() + this.tg.offsetHeight
return this.topLimit() + this.tg.clientHeight
},
checkEdges: function () {
this.topEdge = this.topLimit();
Expand Down Expand Up @@ -1581,6 +1571,7 @@

});


(function(factory){

// CommonJS/RequireJS and "native" compatibility
Expand Down
4 changes: 2 additions & 2 deletions dist/bootstrap-native.min.js

Large diffs are not rendered by default.

33 changes: 12 additions & 21 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -686,30 +686,20 @@ <h4>Basic Usage</h4>
&lt;/div&gt;
</code></pre>

<p>The <code>../assets/js/scripts.js</code> file has some console log functions bound by the change of the <code>.btn-group</code> components.</p>
<pre><code class="language-javascript">//grab all checkboxes
var checkBtns = document.querySelectorAll('input[type="checkbox"]');

//attach a handler to first
checkBtns[0].addEventListener('change',function() {
// do your stuff when first checkbox changes
})

// OR
//attach a handler to the first btn-group
document.querySelectorAll('[data-toggle="buttons"]')[0].addEventListener('change', function(e) {
console.log( 'The button group with checkboxes inside changed' );
//do your stuff when btn-group changed
});
<p>The <code>../assets/js/scripts.js</code> file has some console log functions bound by the change of the <code>.btn-group</code> components. When using polyfills and since the <code>change</code> event isn't fired properly on legacy browsers, you can make use of the custom <code>bs.button.change</code> event to bind into context other functions. Example:</p>
<pre><code class="language-javascript">//let's do some custom binding action
myCheckBoxesGroup.addEventListener('bs.button.change', function() {
// do something when anything inside the btn-group changes
});
myRadioInput.addEventListener('bs.button.change',function() {
// do something only when this specific input changes
});
</code></pre>

<h4>Additional Notes</h4>
<p>
Button will activate automatically for all <code>.btn-group</code> component instances if they have <code>data-toggle="buttons"</code> and the appropriate input elements inside like our examples here.
The script properly covers the "change" event for checkbox and radio buttons, allowing other functions to bind into context. The event is also bound to the <code>.btn-group</code> component.
The script doesn't have any method to control it's toggle functions for checkbox and radio buttons as described by the documentation of the original plugin.
</p>
<p>Button also doesn't cover <a href="http://getbootstrap.com/javascript/#buttons-single-toggle" target="_blank">single toggle</a>.</p>
<p>Button will activate automatically for all <code>.btn-group</code> component instances if they have <code>data-toggle="buttons"</code> and the appropriate input elements inside like our examples here.</p>
<p>The script properly covers the "change" event for checkbox and radio buttons, but via a custom event called <code>bs.button.change</code>, allowing other functions to bind into context. The event is also bound to the <code>.btn-group</code> component, but to use it, you have to use polyfills for legacy browsers.</p>
<p>The script doesn't have any method to control it's toggle functions for checkbox and radio buttons as described by the documentation of the original plugin. Button also doesn't cover <a href="http://getbootstrap.com/javascript/#buttons-single-toggle" target="_blank">single toggle</a>.</p>

</section>

Expand Down Expand Up @@ -855,6 +845,7 @@ <h3>This is another caption</h3>
</code></pre>
<h4>Additional Notes</h4>
<p>Carousel does not use DATA API for previous and next navigation controls, which means you can ignore <code>data-slide="prev"</code> and <code>data-slide="next"</code> attributes, but they must have at least <code>class="carousel-control"</code>.</p>
<p>The script properly covers both the original custom events <code>slid.bs.carousel</code> and <code>slide.bs.carousel</code>, allowing other functions to bind into context. To use it, you have to use polyfills for legacy browsers.</p>
</section>

<section id="exampleAffix">
Expand Down
11 changes: 6 additions & 5 deletions lib/button-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@
},

actions : function() {
var self = this, isIE = /ie/.test(document.documentElement.className),
changeEvent = (('Event' in window) && window.dispatchEvent) ? new Event('change') : null; // The native event that will be triggered on demand for IE
var self = this,
changeEvent = (('CustomEvent' in window) && window.dispatchEvent)
? new CustomEvent('bs.button.change') : null; // The custom event that will be triggered on demand

// assign event to a trigger function
function triggerChange(t) { if (changeEvent) { t.dispatchEvent(changeEvent); } }
Expand Down Expand Up @@ -101,8 +102,8 @@
input.getAttribute('checked');
input.removeAttribute('checked');
}
if (isIE) { triggerChange(input); } //trigger the change for the input
if (isIE) { triggerChange(self.btn); } //trigger the change for the btn-group
triggerChange(input); //trigger the change for the input
triggerChange(self.btn); //trigger the change for the btn-group
}

if ( input.type === 'radio' ) { // radio buttons
Expand All @@ -111,7 +112,7 @@
input.setAttribute('checked','checked');

triggerChange(self.btn);
if (isIE) { triggerChange(input); } //trigger the change
triggerChange(input); //trigger the change

for (i;i<ll;i++) {
var l = labels[i];
Expand Down
Loading

0 comments on commit 7600c97

Please sign in to comment.