1. Detaching all events, including swipe, when the offcanvas is not meant to be opened. 2. Ensuring to always attach the mutation events when detaching the rest. 3. Code reorganization and cleanup

This commit is contained in:
Djamil Legato
2015-04-17 11:02:04 -07:00
parent f567f128d0
commit fc132f1eef
2 changed files with 280 additions and 209 deletions
+131 -103
View File
@@ -55,6 +55,7 @@ var Offcanvas = new prime({
constructor: function(options) {
this.setOptions(options);
this.attached = false;
this.opening = false;
this.moved = false;
this.dragging = false;
@@ -65,6 +66,9 @@ var Offcanvas = new prime({
current: 0
};
this.bodyEl = $('body');
this.htmlEl = $('html');
this.panel = $('#g-page-surround');
this.offcanvas = $('#g-offcanvas');
@@ -80,21 +84,22 @@ var Offcanvas = new prime({
this.tolerance = typeof this.options.tolerance == 'function' ? this.options.tolerance.call(this, this.options.padding) : this.options.tolerance;
if (this.options.touch && hasTouchEvents) {
this._touchEvents();
}
this.attach();
this._checkTogglers();
return this.attach();
return this;
},
attach: function() {
var body = $('body');
this.attached = true;
if (this.options.touch && hasTouchEvents) {
this.attachTouchEvents();
}
forEach(['toggle', 'open', 'close'], bind(function(mode) {
body.delegate('click', '[data-offcanvas-' + mode + ']', this.bound(mode));
if (hasTouchEvents) { body.delegate('touchend', '[data-offcanvas-' + mode + ']', this.bound(mode)); }
this.bodyEl.delegate('click', '[data-offcanvas-' + mode + ']', this.bound(mode));
if (hasTouchEvents) { this.bodyEl.delegate('touchend', '[data-offcanvas-' + mode + ']', this.bound(mode)); }
}, this));
this.attachMutationEvent();
@@ -108,12 +113,32 @@ var Offcanvas = new prime({
this.offcanvas.on('DOMSubtreeModified', this.bound('_checkTogglers')); // IE8 < has propertychange
},
attachTouchEvents: function() {
var msPointerSupported = window.navigator.msPointerEnabled,
touch = {
start: msPointerSupported ? 'MSPointerDown' : 'touchstart',
move: msPointerSupported ? 'MSPointerMove' : 'touchmove',
end: msPointerSupported ? 'MSPointerUp' : 'touchend'
};
this._scrollBound = decouple(this.bodyEl, 'scroll', this.bound('_bodyScroll'));
this.bodyEl.on(touch.move, this.bound('_bodyMove'));
this.panel.on(touch.start, this.bound('_touchStart'));
this.panel.on('touchcancel', this.bound('_touchCancel'));
this.panel.on(touch.end, this.bound('_touchEnd'));
this.panel.on(touch.move, this.bound('_touchMove'));
},
detach: function() {
var body = $('body');
this.attached = false;
if (this.options.touch && hasTouchEvents) {
this.detachTouchEvents();
}
forEach(['toggle', 'open', 'close'], bind(function(mode) {
body.undelegate('click', '[data-offcanvas-' + mode + ']', this.bound(mode));
if (hasTouchEvents) { body.undelegate('touchend', '[data-offcanvas-' + mode + ']', this.bound(mode)); }
this.bodyEl.undelegate('click', '[data-offcanvas-' + mode + ']', this.bound(mode));
if (hasTouchEvents) { this.bodyEl.undelegate('touchend', '[data-offcanvas-' + mode + ']', this.bound(mode)); }
}, this));
this.detachMutationEvent();
@@ -127,6 +152,22 @@ var Offcanvas = new prime({
this.offcanvas.off('DOMSubtreeModified', this.bound('_checkTogglers'));
},
detachTouchEvents: function() {
var msPointerSupported = window.navigator.msPointerEnabled,
touch = {
start: msPointerSupported ? 'MSPointerDown' : 'touchstart',
move: msPointerSupported ? 'MSPointerMove' : 'touchmove',
end: msPointerSupported ? 'MSPointerUp' : 'touchend'
};
this.bodyEl[0].removeEventListener('scroll', this._scrollBound);
this.bodyEl.off(touch.move, this.bound('_bodyMove'));
this.panel.off(touch.start, this.bound('_touchStart'));
this.panel.off('touchcancel', this.bound('_touchCancel'));
this.panel.off(touch.end, this.bound('_touchEnd'));
this.panel.off(touch.move, this.bound('_touchMove'));
},
open: function(event) {
if (event && event.type.match(/^touch/i)) { event.preventDefault(); }
@@ -134,17 +175,14 @@ var Offcanvas = new prime({
if (this.opened) { return this; }
var html = $('html'),
body = $('body');
if (!html.hasClass(this.options.openClass)) {
html.addClass(this.options.openClass);
if (!this.htmlEl.hasClass(this.options.openClass)) {
this.htmlEl.addClass(this.options.openClass);
}
this.overlay[0].style.opacity = 1;
this._setTransition();
this._translateXTo((body.hasClass('g-offcanvas-right') ? -1 : 1) * this.options.padding);
this._translateXTo((this.bodyEl.hasClass('g-offcanvas-right') ? -1 : 1) * this.options.padding);
this.opened = true;
setTimeout(bind(function() {
@@ -165,8 +203,6 @@ var Offcanvas = new prime({
if (!this.opened && !this.opening) { return this; }
if (this.panel !== element && this.dragging) { return false; }
var html = $('html');
this.overlay[0].style.opacity = 0;
this._setTransition();
@@ -176,7 +212,7 @@ var Offcanvas = new prime({
setTimeout(bind(function() {
var panel = this.panel[0];
html.removeClass(this.options.openClass);
this.htmlEl.removeClass(this.options.openClass);
panel.style.transition = panel.style['-webkit-transition'] = '';
}, this), this.options.duration);
@@ -204,99 +240,85 @@ var Offcanvas = new prime({
panel.style[prefix.css + 'transform'] = panel.style.transform = 'translate3d(' + x + 'px, 0, 0)';
},
_touchEvents: function() {
var msPointerSupported = window.navigator.msPointerEnabled,
self = this,
html = $('html'),
body = $('body'),
touch = {
start: msPointerSupported ? 'MSPointerDown' : 'touchstart',
move: msPointerSupported ? 'MSPointerMove' : 'touchmove',
end: msPointerSupported ? 'MSPointerUp' : 'touchend'
};
_bodyScroll: function() {
if (!this.moved) {
clearTimeout(scrollTimeout);
isScrolling = true;
scrollTimeout = setTimeout(function() {
isScrolling = false;
}, 250);
}
},
decouple(body, 'scroll', function() {
if (!self.moved) {
clearTimeout(scrollTimeout);
isScrolling = true;
scrollTimeout = setTimeout(function() {
isScrolling = false;
}, 250);
}
});
_bodyMove: function() {
if (this.moved) { event.preventDefault(); }
this.dragging = true;
},
body.on(touch.move, function(event) {
if (self.moved) { event.preventDefault(); }
self.dragging = true;
});
_touchStart: function(event) {
if (!event.touches) { return; }
this.panel.on(touch.start, function(event) {
if (!event.touches) { return; }
this.moved = false;
this.opening = false;
this.dragging = false;
this.offsetX.start = event.touches[0].pageX;
this.preventOpen = (!this.opened && this.offcanvas[0].clientWidth !== 0);
},
self.moved = false;
self.opening = false;
self.dragging = false;
self.offsetX.start = event.touches[0].pageX;
self.preventOpen = (!self.opened && self.offcanvas[0].clientWidth !== 0);
});
_touchCancel: function() {
this.moved = false;
this.opening = false;
},
this.panel.on('touchcancel', function() {
self.moved = false;
self.opening = false;
});
_touchMove: function(event) {
if (isScrolling || this.preventOpen || !event.touches) { return; }
this.panel.on(touch.end, function(event) {
var placement = (this.bodyEl.hasClass('g-offcanvas-right') ? -1 : 1), // 1: left, -1: right
place = placement < 0 ? 'right' : 'left',
diffX = clamp(event.touches[0].clientX - this.offsetX.start, -this.options.padding, this.options.padding),
translateX = this.offsetX.current = diffX,
overlayOpacity;
if (self.moved) {
var tolerance = Math.abs(self.offsetX.current) > self.tolerance,
placement = body.hasClass('g-offcanvas-right') ? true : false,
direction = !placement ? (self.offsetX.current < 0) : (self.offsetX.current > 0);
if (Math.abs(translateX) > this.options.padding) { return; }
if (Math.abs(diffX) > 0) {
this.opening = true;
self.opening = tolerance ? !direction : direction;
self.opened = !self.opening;
self[self.opening ? 'open' : 'close'](event, self.panel);
// offcanvas on left
if (place == 'left' && (this.opened && diffX > 0 || !this.opened && diffX < 0)) { return; }
// offcanvas on right
if (place == 'right' && (this.opened && diffX < 0 || !this.opened && diffX > 0)) { return; }
if (!this.moved && !this.htmlEl.hasClass(this.options.openClass)) {
this.htmlEl.addClass(this.options.openClass);
}
self.moved = false;
});
this.panel.on(touch.move, function(event) {
if (isScrolling || self.preventOpen || !event.touches) { return; }
var placement = (body.hasClass('g-offcanvas-right') ? -1 : 1), // 1: left, -1: right
place = placement < 0 ? 'right' : 'left',
diffX = clamp(event.touches[0].clientX - self.offsetX.start, -self.options.padding, self.options.padding),
translateX = self.offsetX.current = diffX,
overlayOpacity;
if (Math.abs(translateX) > self.options.padding) { return; }
if (Math.abs(diffX) > 0) {
self.opening = true;
// offcanvas on left
if (place == 'left' && (self.opened && diffX > 0 || !self.opened && diffX < 0)) { return; }
// offcanvas on right
if (place == 'right' && (self.opened && diffX < 0 || !self.opened && diffX > 0)) { return; }
if (!self.moved && !html.hasClass(self.options.openClass)) {
html.addClass(self.options.openClass);
}
if ((place == 'left' && diffX <= 0) || (place == 'right' && diffX >= 0)) {
translateX = diffX + (placement * self.options.padding);
self.opening = false;
}
overlayOpacity = mapNumber(Math.abs(translateX), 0, self.options.padding, 0, 1);
self.panel[0].style[prefix.css + 'transform'] = self.panel[0].style.transform = 'translate3d(' + translateX + 'px, 0, 0)';
self.overlay[0].style.opacity = overlayOpacity;
self.moved = true;
if ((place == 'left' && diffX <= 0) || (place == 'right' && diffX >= 0)) {
translateX = diffX + (placement * this.options.padding);
this.opening = false;
}
});
overlayOpacity = mapNumber(Math.abs(translateX), 0, this.options.padding, 0, 1);
this.panel[0].style[prefix.css + 'transform'] = this.panel[0].style.transform = 'translate3d(' + translateX + 'px, 0, 0)';
this.overlay[0].style.opacity = overlayOpacity;
this.moved = true;
}
},
_touchEnd: function(event) {
if (this.moved) {
var tolerance = Math.abs(this.offsetX.current) > this.tolerance,
placement = this.bodyEl.hasClass('g-offcanvas-right') ? true : false,
direction = !placement ? (this.offsetX.current < 0) : (this.offsetX.current > 0);
this.opening = tolerance ? !direction : direction;
this.opened = !this.opening;
this[this.opening ? 'open' : 'close'](event, this.panel);
}
this.moved = false;
},
_checkTogglers: function(mutator) {
@@ -313,8 +335,14 @@ var Offcanvas = new prime({
if (!togglers || (mutator && ((mutator.target || mutator.srcElement) !== mobileContainer[0]))) { return; }
if (this.opened) { this.close(); }
var check = (blocks && blocks.length == 1) && mobileContainer && !trim(this.offcanvas.text()).length;
togglers[check ? 'addClass' : 'removeClass']('g-offcanvas-hide');
var shouldCollapse = (blocks && blocks.length == 1) && mobileContainer && !trim(this.offcanvas.text()).length;
togglers[shouldCollapse ? 'addClass' : 'removeClass']('g-offcanvas-hide');
if (!shouldCollapse && !this.attached) { this.attach(); }
else if (shouldCollapse && this.attached) {
this.detach();
this.attachMutationEvent();
}
}
});