Related
is possible to change a name of a function after resizing screen?
I have this
<div id="box1" data-same-height="blocks-resize">
where
data-same-height="blocks-resize"
is the function, and here's the javascript of that function.
$(document).ready(function() {
var equalize = function () {
var disableOnMaxWidth = 0; // 767 for bootstrap
var grouped = {};
var elements = $('*[data-same-height]');
elements.each(function () {
var el = $(this);
var id = el.attr('data-same-height');
if (!grouped[id]) {
grouped[id] = [];
}
grouped[id].push(el);
});
$.each(grouped, function (key) {
var elements = $('*[data-same-height="' + key + '"]');
elements.css('height', '');
var winWidth = $(window).width();
if (winWidth <= disableOnMaxWidth) {
return;
}
var maxHeight = 0;
elements.each(function () {
var eleq = $(this);
maxHeight = Math.max(eleq.height(), maxHeight);
});
elements.css('height', maxHeight + "px");
});
};
var timeout = null;
$(window).resize(function () {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(equalize, 250);
});
equalize();
});
It's possible to change that name (data-same-height) after resizing screen to 600px? or could be also the "blocks-resize" too
Thanks!
$(function(){
$(window).on("resize load", responsive);
})
var responsive = function() {
if(window.innerWidth < 600) {
//do your thing
} else {
//do something else.
}
}
I used a theme that I changed to show sub menu horizontally, now I want to show sub menu onClick and not on hover, I'm not good at javascript but I think that this code is about the navigation menu:
var nav = {
init: function() {
// Add parent class to items with sub-menus
jQuery("ul.sub-menu").parent().addClass('parent');
var menuTop = 40;
var menuTopReset = 80;
// Enable hover dropdowns for window size above tablet width
jQuery("nav").find(".menu li.parent").hoverIntent({
over: function() {
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
// Setup menuLeft variable, with main menu value
var subMenuWidth = jQuery(this).find('ul.sub-menu').first().outerWidth(true);
var mainMenuItemWidth = jQuery(this).outerWidth(true);
var menuLeft = '-' + (Math.round(subMenuWidth / 2) - Math.round(mainMenuItemWidth / 2)) + 'px';
var menuContainer = jQuery(this).parent().parent();
// Check if this is the top bar menu
if (menuContainer.hasClass("top-menu")) {
if (menuContainer.parent().parent().parent().hasClass("top-bar-menu-right")) {
menuLeft = "";
} else {
menuLeft = "-1px";
}
menuTop = 30;
menuTopReset = 40;
} else if (menuContainer.hasClass("header-menu")) {
menuLeft = "-1px";
menuTop = 28;
menuTopReset = 40;
} else if (menuContainer.hasClass("mini-menu") || menuContainer.parent().hasClass("mini-menu")) {
menuTop = 40;
menuTopReset = 58;
} else {
menuTop = 44;
menuTopReset = 64;
}
// Check if second level dropdown
if (jQuery(this).find('ul.sub-menu').first().parent().parent().hasClass("sub-menu")) {
menuLeft = jQuery(this).find('ul.sub-menu').first().parent().parent().outerWidth(true) - 2;
}
jQuery(this).find('ul.sub-menu').first().addClass('show-dropdown').css('top', menuTop);
}
},
out:function() {
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
jQuery(this).find('ul.sub-menu').first().removeClass('show-dropdown').css('top', menuTopReset);
}
}
});
jQuery(".shopping-bag-item").live("mouseenter", function() {
var subMenuTop = 44;
if (jQuery(this).parent().parent().hasClass("mini-menu")) {
subMenuTop = 40;
}
jQuery(this).find('ul.sub-menu').first().addClass('show-dropdown').css('top', subMenuTop);
}).live("mouseleave", function() {
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
jQuery(this).find('ul.sub-menu').first().removeClass('show-dropdown').css('top', 64);
}
});
// Toggle Mobile Nav show/hide
jQuery('a.show-main-nav').on('click', function(e) {
e.preventDefault();
if (jQuery('#main-navigation').is(':visible')) {
jQuery('.header-overlay .header-wrap').css('position', '');
} else {
jQuery('.header-overlay .header-wrap').css('position', 'relative');
}
jQuery('#main-navigation').toggle();
});
jQuery(window).smartresize(function(){
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
var menus = jQuery('nav').find('ul.menu');
menus.each(function() {
jQuery(this).css("display", "");
});
}
});
// Set current language to top bar item
var currentLanguage = jQuery('li.aux-languages').find('.current-language span').text();
if (currentLanguage !== "") {
jQuery('li.aux-languages > a').text(currentLanguage);
}
},
hideNav: function(subnav) {
setTimeout(function() {
if (subnav.css("opacity") === "0") {
subnav.css("display", "none");
}
}, 300);
}
};
I tried to replace "hoverIntent" by "click" but it doesn't work, what can I do?
What's happening when someone currently hovers, it does one thing while hovering and when they leave it has to d a sort of cleanup which are the two functions within hoverintent, namely over and out so the code would need to be split into two event listeners one for click of the element and one for blur
I have chained the two listeners to the inital selector so it should all work.
var nav = {
init: function() {
// Add parent class to items with sub-menus
jQuery("ul.sub-menu").parent().addClass('parent');
var menuTop = 40;
var menuTopReset = 80;
// Enable click dropdowns for window size above tablet width
jQuery("nav").find(".menu li.parent").on('click', function (event) {
if($(event.target).parent().hasClass('menu-item-has-children')){
event.preventDefault();
};
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
// Setup menuLeft variable, with main menu value
var subMenuWidth = jQuery(this).find('ul.sub-menu').first().outerWidth(true);
var mainMenuItemWidth = jQuery(this).outerWidth(true);
var menuLeft = '-' + (Math.round(subMenuWidth / 2) - Math.round(mainMenuItemWidth / 2)) + 'px';
var menuContainer = jQuery(this).parent().parent();
// Check if this is the top bar menu
if (menuContainer.hasClass("top-menu")) {
if (menuContainer.parent().parent().parent().hasClass("top-bar-menu-right")) {
menuLeft = "";
} else {
menuLeft = "-1px";
}
menuTop = 30;
menuTopReset = 40;
} else if (menuContainer.hasClass("header-menu")) {
menuLeft = "-1px";
menuTop = 28;
menuTopReset = 40;
} else if (menuContainer.hasClass("mini-menu") || menuContainer.parent().hasClass("mini-menu")) {
menuTop = 40;
menuTopReset = 58;
} else {
menuTop = 44;
menuTopReset = 64;
}
// Check if second level dropdown
if (jQuery(this).find('ul.sub-menu').first().parent().parent().hasClass("sub-menu")) {
menuLeft = jQuery(this).find('ul.sub-menu').first().parent().parent().outerWidth(true) - 2;
}
jQuery(this).find('ul.sub-menu').first().addClass('show-dropdown').css('top', menuTop);
}
}).on('mouseleave',function () {
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
jQuery(this).find('ul.sub-menu').first().removeClass('show-dropdown').css('top', menuTopReset);
}
});
// Toggle Mobile Nav show/hide
jQuery('a.show-main-nav').on('click', function(e) {
e.preventDefault();
if (jQuery('#main-navigation').is(':visible')) {
jQuery('.header-overlay .header-wrap').css('position', '');
} else {
jQuery('.header-overlay .header-wrap').css('position', 'relative');
}
jQuery('#main-navigation').toggle();
});
jQuery(window).smartresize(function(){
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
var menus = jQuery('nav').find('ul.menu');
menus.each(function() {
jQuery(this).css("display", "");
});
}
});
// Set current language to top bar item
var currentLanguage = jQuery('li.aux-languages').find('.current-language span').text();
if (currentLanguage !== "") {
jQuery('li.aux-languages > a').text(currentLanguage);
}
},
hideNav: function(subnav) {
setTimeout(function() {
if (subnav.css("opacity") === "0") {
subnav.css("display", "none");
}
}, 300);
}
};
That seems overly complicated. Do you have a link to the live version?
Usually, when doing a click-to-see submenu with a clickable parent, I set a variable to see whether the menu is open, if not dont go to link. if so, go to link.
An example: http://codepen.io/jhealey5/pen/iLgom
var $handle = $('.sub-menu').prev();
var opened = 0;
$handle.on('click', function(e){
if (opened) {
window.location.href = $(this).attr('href');
} else {
e.preventDefault();
e.stopPropagation();
$('.sub-menu').slideToggle();
opened = 1;
}
});
$('html').on('click', function(){
if (opened) {
$('.sub-menu').slideToggle();
opened = 0;
}
});
Depending on that menu of yours, you could use something similar. But it's using a lot of code for a menu.
Something in this Curtains.js plug-in is preventing user text selection on my page. When I comment it out, I'm able to select text, when I put it back in, I'm not. Can someone identify it and tell me how to fix it? I'm at my wit's end.
<script>
/*
* Curtain.js - Create an unique page transitioning system
* ---
* Version: 2
* Copyright 2011, Victor Coulon (http://victorcoulon.fr)
* Released under the MIT Licence
*/
(function ( $, window, document, undefined ) {
var pluginName = 'curtain',
defaults = {
scrollSpeed: 400,
bodyHeight: 0,
linksArray: [],
mobile: false,
scrollButtons: {},
controls: null,
curtainLinks: '.curtain-links',
enableKeys: true,
easing: 'swing',
disabled: false,
nextSlide: function() {},
prevSlide: function() {}
};
// The actual plugin constructor
function Plugin( element, options ) {
var self = this;
// Public attributes
this.element = element;
this.options = $.extend( {}, defaults, options) ;
this._defaults = defaults;
this._name = pluginName;
this._ignoreHashChange = false;
this.init();
}
Plugin.prototype = {
init: function () {
var self = this;
// Cache element
this.$element = $(this.element);
this.$li = $(this.element).find('>li');
this.$liLength = this.$li.length;
self.$windowHeight = $(window).height();
self.$elDatas = {};
self.$document = $(document);
self.$window = $(window);
self.webkit = (navigator.userAgent.indexOf('Chrome') > -1 || navigator.userAgent.indexOf("Safari") > -1);
$.Android = (navigator.userAgent.match(/Android/i));
$.iPhone = ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)));
$.iPad = ((navigator.userAgent.match(/iPad/i)));
$.iOs4 = (/OS [1-4]_[0-9_]+ like Mac OS X/i.test(navigator.userAgent));
if($.iPhone || $.iPad || $.Android || self.options.disabled){
this.options.mobile = true;
this.$li.css({position:'relative'});
this.$element.find('.fixed').css({position:'absolute'});
}
if(this.options.mobile){
this.scrollEl = this.$element;
} else if($.browser.mozilla || $.browser.msie) {
this.scrollEl = $('html');
} else {
this.scrollEl = $('body');
}
if(self.options.controls){
self.options.scrollButtons['up'] = $(self.options.controls).find('[href="#up"]');
self.options.scrollButtons['down'] = $(self.options.controls).find('[href="#down"]');
if(!$.iOs4 && ($.iPhone || $.iPad)){
self.$element.css({
position:'fixed',
top:0,
left:0,
right:0,
bottom:0,
'-webkit-overflow-scrolling':'touch',
overflow:'auto'
});
$(self.options.controls).css({position:'absolute'});
}
}
// When all image is loaded
var callbackImageLoaded = function(){
self.setDimensions();
self.$li.eq(0).addClass('current');
self.setCache();
if(!self.options.mobile){
if(self.$li.eq(1).length)
self.$li.eq(1).nextAll().addClass('hidden');
}
self.setEvents();
self.setLinks();
self.isHashIsOnList(location.hash.substring(1));
};
if(self.$element.find('img').length)
self.imageLoaded(callbackImageLoaded);
else
callbackImageLoaded();
},
// Events
scrollToPosition: function (direction){
var position = null,
self = this;
if(self.scrollEl.is(':animated')){
return false;
}
if(direction === 'up' || direction == 'down'){
// Keyboard event
var $next = (direction === 'up') ? self.$current.prev() : self.$current.next();
// Step in the current panel ?
if(self.$step){
if(!self.$current.find('.current-step').length){
self.$step.eq(0).addClass('current-step');
}
var $nextStep = (direction === 'up') ? self.$current.find('.current-step').prev('.step') : self.$current.find('.current-step').next('.step');
if($nextStep.length) {
position = (self.options.mobile) ? $nextStep.position().top + self.$elDatas[self.$current.index()]['data-position'] : $nextStep.position().top + self.$elDatas[self.$current.index()]['data-position'];
}
}
position = position || ((self.$elDatas[$next.index()] === undefined) ? null : self.$elDatas[$next.index()]['data-position']);
if(position !== null){
self.scrollEl.animate({
scrollTop: position
}, self.options.scrollSpeed, self.options.easing);
}
} else if(direction === 'top'){
self.scrollEl.animate({
scrollTop:0
}, self.options.scrollSpeed, self.options.easing);
} else if(direction === 'bottom'){
self.scrollEl.animate({
scrollTop:self.options.bodyHeight
}, self.options.scrollSpeed, self.options.easing);
} else {
var index = $("#"+direction).index(),
speed = Math.abs(self.currentIndex-index) * (this.options.scrollSpeed*4) / self.$liLength;
self.scrollEl.animate({
scrollTop:self.$elDatas[index]['data-position'] || null
}, (speed <= self.options.scrollSpeed) ? self.options.scrollSpeed : speed, this.options.easing);
}
},
scrollEvent: function() {
var self = this,
docTop = self.$document.scrollTop();
if(docTop < self.currentP && self.currentIndex > 0){
// Scroll to top
self._ignoreHashChange = true;
if(self.$current.prev().attr('id'))
self.setHash(self.$current.prev().attr('id'));
self.$current
.removeClass('current')
.css( (self.webkit) ? {'-webkit-transform': 'translateY(0px) translateZ(0)'} : {marginTop: 0} )
.nextAll().addClass('hidden').end()
.prev().addClass('current').removeClass('hidden');
self.setCache();
self.options.prevSlide();
} else if(docTop < (self.currentP + self.currentHeight)){
// Animate the current pannel during the scroll
if(self.webkit)
self.$current.css({'-webkit-transform': 'translateY('+(-(docTop-self.currentP))+'px) translateZ(0)' });
else
self.$current.css({marginTop: -(docTop-self.currentP) });
// If there is a fixed element in the current panel
if(self.$fixedLength){
var dataTop = parseInt(self.$fixed.attr('data-top'), 10);
if(docTop + self.$windowHeight >= self.currentP + self.currentHeight){
self.$fixed.css({
position: 'fixed'
});
} else {
self.$fixed.css({
position: 'absolute',
marginTop: Math.abs(docTop-self.currentP)
});
}
}
// If there is a step element in the current panel
if(self.$stepLength){
$.each(self.$step, function(i,el){
if(($(el).position().top+self.currentP) <= docTop+5 && $(el).position().top + self.currentP + $(el).height() >= docTop+5){
if(!$(el).hasClass('current-step')){
self.$step.removeClass('current-step');
$(el).addClass('current-step');
return false;
}
}
});
}
if(self.parallaxBg){
self.$current.css({
'background-position-y': docTop * self.parallaxBg
});
}
if(self.$fade.length){
self.$fade.css({
'opacity': 1-(docTop/ self.$fade.attr('data-fade'))
});
}
if(self.$slowScroll.length){
self.$slowScroll.css({
'margin-top' : (docTop / self.$slowScroll.attr('data-slow-scroll'))
});
}
} else {
// Scroll bottom
self._ignoreHashChange = true;
if(self.$current.next().attr('id'))
self.setHash(self.$current.next().attr('id'));
self.$current.removeClass('current')
.addClass('hidden')
.next('li').addClass('current').next('li').removeClass('hidden');
self.setCache();
self.options.nextSlide();
}
},
scrollMobileEvent: function() {
var self = this,
docTop = self.$element.scrollTop();
if(docTop+10 < self.currentP && self.currentIndex > 0){
// Scroll to top
self._ignoreHashChange = true;
if(self.$current.prev().attr('id'))
self.setHash(self.$current.prev().attr('id'));
self.$current.removeClass('current').prev().addClass('current');
self.setCache();
self.options.prevSlide();
} else if(docTop+10 < (self.currentP + self.currentHeight)){
// If there is a step element in the current panel
if(self.$stepLength){
$.each(self.$step, function(i,el){
if(($(el).position().top+self.currentP) <= docTop && (($(el).position().top+self.currentP) + $(el).outerHeight()) >= docTop){
if(!$(el).hasClass('current-step')){
self.$step.removeClass('current-step');
$(el).addClass('current-step');
}
}
});
}
} else {
// Scroll bottom
self._ignoreHashChange = true;
if(self.$current.next().attr('id'))
self.setHash(self.$current.next().attr('id'));
self.$current.removeClass('current').next().addClass('current');
self.setCache();
self.options.nextSlide();
}
},
// Setters
setDimensions: function(){
var self = this,
levelHeight = 0,
cover = false,
height = null;
self.$windowHeight = self.$window.height();
this.$li.each(function(index) {
var $self = $(this);
cover = $self.hasClass('cover');
if(cover){
$self.css({height: self.$windowHeight, zIndex: 999-index})
.attr('data-height',self.$windowHeight)
.attr('data-position',levelHeight);
self.$elDatas[$self.index()] = {
'data-height': parseInt(self.$windowHeight,10),
'data-position': parseInt(levelHeight, 10)
};
levelHeight += self.$windowHeight;
} else{
height = ($self.outerHeight() <= self.$windowHeight) ? self.$windowHeight : $self.outerHeight();
$self.css({minHeight: height, zIndex: 999-index})
.attr('data-height',height)
.attr('data-position',levelHeight);
self.$elDatas[$self.index()] = {
'data-height': parseInt(height, 10),
'data-position': parseInt(levelHeight, 10)
};
levelHeight += height;
}
if($self.find('.fixed').length){
var top = $self.find('.fixed').css('top');
$self.find('.fixed').attr('data-top', top);
}
});
if(!this.options.mobile)
this.setBodyHeight();
},
setEvents: function() {
var self = this;
$(window).on('resize', function(){
self.setDimensions();
});
if(self.options.mobile) {
self.$element.on('scroll', function(){
self.scrollMobileEvent();
});
} else {
self.$window.on('scroll', function(){
self.scrollEvent();
});
}
if(self.options.enableKeys) {
self.$document.on('keydown', function(e){
if(e.keyCode === 38 || e.keyCode === 37) {
self.scrollToPosition('up');
e.preventDefault();
return false;
}
if(e.keyCode === 40 || e.keyCode === 39){
self.scrollToPosition('down');
e.preventDefault();
return false;
}
// Home button
if(e.keyCode === 36){
self.scrollToPosition('top');
e.preventDefault();
return false;
}
// End button
if(e.keyCode === 35){
self.scrollToPosition('bottom');
e.preventDefault();
return false;
}
});
}
if(self.options.scrollButtons){
if(self.options.scrollButtons.up){
self.options.scrollButtons.up.on('click', function(e){
e.preventDefault();
self.scrollToPosition('up');
});
}
if(self.options.scrollButtons.down){
self.options.scrollButtons.down.on('click', function(e){
e.preventDefault();
self.scrollToPosition('down');
});
}
}
if(self.options.curtainLinks){
$(self.options.curtainLinks).on('click', function(e){
e.preventDefault();
var href = $(this).attr('href');
if(!self.isHashIsOnList(href.substring(1)) && position)
return false;
var position = self.$elDatas[$(href).index()]['data-position'] || null;
if(position){
self.scrollEl.animate({
scrollTop:position
}, self.options.scrollSpeed, self.options.easing);
}
return false;
});
}
self.$window.on("hashchange", function(event){
if(self._ignoreHashChange === false){
self.isHashIsOnList(location.hash.substring(1));
}
self._ignoreHashChange = false;
});
},
setBodyHeight: function(){
var h = 0;
for (var key in this.$elDatas) {
var obj = this.$elDatas[key];
h += obj['data-height'];
}
this.options.bodyHeight = h;
$('body').height(h);
},
setLinks: function(){
var self = this;
this.$li.each(function() {
var id = $(this).attr('id') || 0;
self.options.linksArray.push(id);
});
},
setHash: function(hash){
// "HARD FIX"
el = $('[href=#'+hash+']');
el.parent().siblings('li').removeClass('active');
el.parent().addClass('active');
if(history.pushState) {
history.pushState(null, null, '#'+hash);
}
else {
location.hash = hash;
}
},
setCache: function(){
var self = this;
self.$current = self.$element.find('.current');
self.$fixed = self.$current.find('.fixed');
self.$fixedLength = self.$fixed.length;
self.$step = self.$current.find('.step');
self.$stepLength = self.$step.length;
self.currentIndex = self.$current.index();
self.currentP = self.$elDatas[self.currentIndex]['data-position'];
self.currentHeight = self.$elDatas[self.currentIndex]['data-height'];
self.parallaxBg = self.$current.attr('data-parallax-background');
self.$fade = self.$current.find('[data-fade]');
self.$slowScroll = self.$current.find('[data-slow-scroll]');
},
// Utils
isHashIsOnList: function(hash){
var self = this;
$.each(self.options.linksArray, function(i,val){
if(val === hash){
self.scrollToPosition(hash);
return false;
}
});
},
readyElement: function(el,callback){
var interval = setInterval(function(){
if(el.length){
callback(el.length);
clearInterval(interval);
}
},60);
},
imageLoaded: function(callback){
var self = this,
elems = self.$element.find('img'),
len = elems.length,
blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
elems.bind('load.imgloaded',function(){
if (--len <= 0 && this.src !== blank || $(this).not(':visible')){
elems.unbind('load.imgloaded');
callback.call(elems,this);
}
}).each(function(){
if (this.complete || this.complete === undefined){
var src = this.src;
this.src = blank;
this.src = src;
}
});
}
};
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
}
});
};
})( jQuery, window, document );
</script>
First you would have to tell us how you are trying to select text (mouse, keyboard, touchscreen, etc.)
I bet my bitcoins on keyboard (but I don't have any).
Must be one of those
self.$document.on('keydown', function(e){
...
e.preventDefault();
which don't even document which keys these numbers stand for.
It's e.preventDefault() which prevents the default browser action from being performed.
If you're in Chrome devtools, you can use
monitorEvents(window, 'key')
to make sense of these.
Of course this bit may help a bit:
keyCode: 38
keyIdentifier: "Up"
So the code could be written readably by use of keyIdentifier instead of keyCode.
I don't know how compatible that would be across browsers.
Be warned that keydown keyCode values are different from keypress values (which actually insert real characters). keydown key codes will vary between keyboard layouts and locales.
See http://unixpapa.com/js/key.html for disgust and enlightenment, but mostly disgust.
Current best solution i have found:
ko.bindingHandlers.clickedIn = (function () {
function getBounds(element) {
var pos = element.offset();
return {
x: pos.left,
x2: pos.left + element.outerWidth(),
y: pos.top,
y2: pos.top + element.outerHeight()
};
}
function hitTest(o, l) {
function getOffset(o) {
for (var r = { l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth, b: o.offsetHeight };
o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
return r.r += r.l, r.b += r.t, r;
}
for (var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j ? l = [l] : l).length; i;
b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r : b.l <= a.r))
&& (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) && (r[r.length] = l[i]));
return j ? !!r.length : r;
}
return {
init: function (element, valueAccessor) {
var target = valueAccessor();
$(document).click(function (e) {
if (element._clickedInElementShowing === false && target()) {
var $element = $(element);
var bounds = getBounds($element);
var possibleOverlays = $("[style*=z-index],[style*=absolute]").not(":hidden");
$.each(possibleOverlays, function () {
if (hitTest(element, this)) {
var b = getBounds($(this));
bounds.x = Math.min(bounds.x, b.x);
bounds.x2 = Math.max(bounds.x2, b.x2);
bounds.y = Math.min(bounds.y, b.y);
bounds.y2 = Math.max(bounds.y2, b.y2);
}
});
if (e.clientX < bounds.x || e.clientX > bounds.x2 ||
e.clientY < bounds.y || e.clientY > bounds.y2) {
target(false);
}
}
element._clickedInElementShowing = false;
});
$(element).click(function (e) {
e.stopPropagation();
});
},
update: function (element, valueAccessor) {
var showing = ko.utils.unwrapObservable(valueAccessor());
if (showing) {
element._clickedInElementShowing = true;
}
}
};
})();
It works by first query for all elements with either z-index or absolute position that are visible. It then hit tests those elemnts against the elemnet I want to hide if click outside. If its a hit I calculate a new bound retacle which takes into acount the overlay bounds.
Its not rock solid, but works. Please feel free to comment if you see problems with above approuch
Old question
I'm using Knockout but this applies to DOM/Javascript in general
Im trying to find a reliable way if detecting of you click outside of a element. My code looks like this
ko.bindingHandlers.clickedIn = {
init: function (element, valueAccessor) {
var target = valueAccessor();
var clickedIn = false;
ko.utils.registerEventHandler(document, "click", function (e) {
if (!clickedIn && element._clickedInElementShowing === false) {
target(e.target == element);
}
clickedIn = false;
element._clickedInElementShowing = false;
});
ko.utils.registerEventHandler(element, "click", function (e) {
clickedIn = true;
});
},
update: function (element, valueAccessor) {
var showing = ko.utils.unwrapObservable(valueAccessor());
if (showing) {
element._clickedInElementShowing = true;
}
}
};
It works by both listening to click on target element and document. If you click on document but not target element you click outside of it. This works, but, not for overlay items like datepickers etc. This is because these are not inside the target element but in the body. Can I fix this? Are there better way of determine if clicking outside of element?
edit: This kind of works, but only if the overlay is smaller than the element i want to monitor
ko.bindingHandlers.clickedIn = {
init: function (element, valueAccessor) {
var target = valueAccessor();
$(document).click(function (e) {
if (element._clickedInElementShowing === false) {
var $element = $(element);
var pos = $element.offset();
if (e.clientX < pos.left || e.clientX > (pos.left + $element.width()) ||
e.clientY < pos.top || e.clientY > (pos.top + $element.height())) {
target(false);
}
}
element._clickedInElementShowing = false;
});
$(element).click(function (e) {
e.stopPropagation();
});
},
update: function (element, valueAccessor) {
var showing = ko.utils.unwrapObservable(valueAccessor());
if (showing) {
element._clickedInElementShowing = true;
}
}
};
I would like a more rock solid approuch
This is how I usually solve it:
http://jsfiddle.net/jonigiuro/KLxnV/
$('.container').on('click', function(e) {
alert('hide the child');
});
$('.child').on('click', function(e) {
alert('do nothing');
e.stopPropagation(); //THIS IS THE IMPORTANT PART
});
I don't know how your overlay items are generated, but you could always check if the click target is a child of the element you want to constrain your clicks to.
I have a button on my wordpress theme homepage that is used for going to the top of the page. I want to hide it when page is fully scrolled to the top. Here is my code:
(function($) {
var version = '#VERSION',
defaults = {
exclude: [],
excludeWithin:[],
offset: 0,
direction: 'top', // one of 'top' or 'left'
scrollElement: null, // jQuery set of elements you wish to scroll (for $.smoothScroll).
// if null (default), $('html, body').firstScrollable() is used.
scrollTarget: null, // only use if you want to override default behavior
beforeScroll: function() {}, // fn(opts) function to be called before scrolling occurs. "this" is the element(s) being scrolled
afterScroll: function() {}, // fn(opts) function to be called after scrolling occurs. "this" is the triggering element
easing: 'swing',
speed: 600,
autoCoefficent: 2 // coefficient for "auto" speed
},
getScrollable = function(opts) {
var scrollable = [],
scrolled = false,
dir = opts.dir && opts.dir == 'left' ? 'scrollLeft' : 'scrollTop';
this.each(function() {
if (this == document || this == window) { return; }
var el = $(this);
if ( el[dir]() > 0 ) {
scrollable.push(this);
} else {
// if scroll(Top|Left) === 0, nudge the element 1px and see if it moves
el[dir](1);
scrolled = el[dir]() > 0;
if ( scrolled ) {
scrollable.push(this);
}
// then put it back, of course
el[dir](0);
}
});
// If no scrollable elements, fall back to <body>,
// if it's in the jQuery collection
// (doing this because Safari sets scrollTop async,
// so can't set it to 1 and immediately get the value.)
if (!scrollable.length) {
this.each(function(index) {
if (this.nodeName === 'BODY') {
scrollable = [this];
}
});
}
// Use the first scrollable element if we're calling firstScrollable()
if ( opts.el === 'first' && scrollable.length > 1 ) {
scrollable = [ scrollable[0] ];
}
return scrollable;
},
isTouch = 'ontouchend' in document;
$.fn.extend({
scrollable: function(dir) {
var scrl = getScrollable.call(this, {dir: dir});
return this.pushStack(scrl);
},
firstScrollable: function(dir) {
var scrl = getScrollable.call(this, {el: 'first', dir: dir});
return this.pushStack(scrl);
},
smoothScroll: function(options) {
options = options || {};
var opts = $.extend({}, $.fn.smoothScroll.defaults, options),
locationPath = $.smoothScroll.filterPath(location.pathname);
this
.unbind('click.smoothscroll')
.bind('click.smoothscroll', function(event) {
var link = this,
$link = $(this),
exclude = opts.exclude,
excludeWithin = opts.excludeWithin,
elCounter = 0, ewlCounter = 0,
include = true,
clickOpts = {},
hostMatch = ((location.hostname === link.hostname) || !link.hostname),
pathMatch = opts.scrollTarget || ( $.smoothScroll.filterPath(link.pathname) || locationPath ) === locationPath,
thisHash = escapeSelector(link.hash);
if ( !opts.scrollTarget && (!hostMatch || !pathMatch || !thisHash) ) {
include = false;
} else {
while (include && elCounter < exclude.length) {
if ($link.is(escapeSelector(exclude[elCounter++]))) {
include = false;
}
}
while ( include && ewlCounter < excludeWithin.length ) {
if ($link.closest(excludeWithin[ewlCounter++]).length) {
include = false;
}
}
}
if ( include ) {
event.preventDefault();
$.extend( clickOpts, opts, {
scrollTarget: opts.scrollTarget || thisHash,
link: link
});
$.smoothScroll( clickOpts );
}
});
return this;
}
});
$.smoothScroll = function(options, px) {
var opts, $scroller, scrollTargetOffset, speed,
scrollerOffset = 0,
offPos = 'offset',
scrollDir = 'scrollTop',
aniProps = {},
aniOpts = {},
scrollprops = [];
if ( typeof options === 'number') {
opts = $.fn.smoothScroll.defaults;
scrollTargetOffset = options;
} else {
opts = $.extend({}, $.fn.smoothScroll.defaults, options || {});
if (opts.scrollElement) {
offPos = 'position';
if (opts.scrollElement.css('position') == 'static') {
opts.scrollElement.css('position', 'relative');
}
}
scrollTargetOffset = px ||
( $(opts.scrollTarget)[offPos]() &&
$(opts.scrollTarget)[offPos]()[opts.direction] ) ||
0;
}
opts = $.extend({link: null}, opts);
scrollDir = opts.direction == 'left' ? 'scrollLeft' : scrollDir;
if ( opts.scrollElement ) {
$scroller = opts.scrollElement;
scrollerOffset = $scroller[scrollDir]();
} else {
$scroller = $('html, body').firstScrollable();
}
aniProps[scrollDir] = scrollTargetOffset + scrollerOffset + opts.offset;
opts.beforeScroll.call($scroller, opts);
speed = opts.speed;
// automatically calculate the speed of the scroll based on distance / coefficient
if (speed === 'auto') {
// if aniProps[scrollDir] == 0 then we'll use scrollTop() value instead
speed = aniProps[scrollDir] || $scroller.scrollTop();
// divide the speed by the coefficient
speed = speed / opts.autoCoefficent;
}
aniOpts = {
duration: speed,
easing: opts.easing,
complete: function() {
opts.afterScroll.call(opts.link, opts);
}
};
if (opts.step) {
aniOpts.step = opts.step;
}
if ($scroller.length) {
$scroller.stop().animate(aniProps, aniOpts);
} else {
opts.afterScroll.call(opts.link, opts);
}
};
$.smoothScroll.version = version;
$.smoothScroll.filterPath = function(string) {
return string
.replace(/^\//,'')
.replace(/(index|default).[a-zA-Z]{3,4}$/,'')
.replace(/\/$/,'');
};
// default options
$.fn.smoothScroll.defaults = defaults;
function escapeSelector (str) {
return str.replace(/(:|\.)/g,'\\$1');
}
})(jQuery);
your help will be highly appreciated.
I'm guessing this is based on user events so something like this to cover mousescroll and scroll
$(window).bind( "mousewheel DOMMouseScroll scroll", function(e){
if (document.body.scrollTop == 0) {
// do this
}
})