I have a series of elements (lets call them '.my-elements') - some load on document ready, while others are loaded later via a pagination script.
I would like to set a variable according to whether or not the mouse is over these elements. The code below works, but I suspect there is a better way... Can I do this so I only have to reference the DOM once?
$(document).on('mouseenter','.my-elements', function(){
mouse_is_inside = true;
});
$(document).on('mouseleave','.my-elements', function(){
mouse_is_inside = false;
});
Thanks!
You can bind to both together and check the event.type:
$(document).on('mouseenter mouseleave', '.my-elements', function (ev) {
mouse_is_inside = ev.type === 'mouseenter';
});
Or, if you want to keep them separate, .on has another syntax that takes an event map:
$(document).on({
mouseenter: function () {
mouse_is_inside = true;
},
mouseleave: function () {
mouse_is_inside = false;
}
}, '.my-elements');
Check out jQuery hover which is the same as:
$(selector).mouseenter(handlerIn).mouseleave(handlerOut);
UPDATE: I just realized you need to persist the events via the on() method. In that case, you can use an event map like so:
.on({
mouseenter: function() {
console.log('enter');
},
mouseleave: function() {
console.log('bye!');
}
})
Almost all jQuery methods return objects, so you can chain them together:
$(document).on('mouseenter','.my-elements', function(){
mouse_is_inside = true;
}).on('mouseleave','.my-elements', function(){
mouse_is_inside = false;
});
You could also try:
$(".my-elements").hover(function(eIn) {
// do work for mouse over
},
function(eOut) {
// do work for mouse out
});
update and correction
realized you need more dynamic lock in which case Jonathan Lonowski's or Derek Hunziker's is perfect
For starters, you can select for your elements instead of document.
$('.my-elements').on('mouseenter', function(){
mouse_is_inside = true;
});
You could try a shortcut notation like this...
$('.my-elements').on('mouseenter mouseleave', function(){
mouse_is_inside = !mouse_is_inside;
});
This will negate the value every time the mouse goes in or out, which should keep the mouse_is_inside variable set to the right value.
$('.my-elements').on('mouseenter mouseleave', function(event){
mouse_is_inside = event.type === 'mouseenter';
});
but its generally not a good idea to have a global variable incidating a event state
Related
I want to fade out object 1 and after fade out remove a class and add one. After that on another object the object 2 should fadein and then i assign it a class. The problem that i encountered is that if i fire my event faster than the fadein/fadeout the object stays active.
$('.menuA').on("click", function () {
$('.menuA').removeClass("blue accent-3 z-depth-2", 100);
let clicked = $(this);
$('.menuA').promise().done(function () {
clicked.addClass("blue accent-3 z-depth-2", 100);
})
animatePanes($(this).attr("con"));
})
function animatePanes(pane) {
let paneOld = $('.pane-active');
paneOld.fadeOut(250).removeClass("pane-active").addClass("pane-inactive").promise().done(function () {
$('.' + pane).fadeIn(250).removeClass("pane-inactive").addClass("pane-active");
});
};
Thats my event with the function. The problem is that if i click to fast and trigger the event on menuA the paneOld doesnt get the class pane-inactive.
I already tried to do a global variable that checks if the event is running but it didnt worked (probably because i thinked wrong).
Is there a way to disable the event listener until the event is completly finished?
Or is there a better way?
You can surely do it with a global variable - as you've already said. Maybe you just put in in the wrong place.
I'd recommend something like isAnimating=false and 'disable' the click event listener if it's value is true. This way you can reset isAnimating to false as soon as all your animations are completed.
var isAnimating = false;
$('.menuA').on("click", function() {
if (!isAnimating) {
$('.menuA').removeClass("blue accent-3 z-depth-2", 100);
let clicked = $(this);
$('.menuA').promise().done(function() {
clicked.addClass("blue accent-3 z-depth-2", 100);
})
animatePanes($(this).attr("con"));
isAnimating = true;
}
})
function animatePanes(pane) {
let paneOld = $('.pane-active');
paneOld.fadeOut(250).removeClass("pane-active").addClass("pane-inactive").promise().done(function() {
$('.' + pane).fadeIn(250).removeClass("pane-inactive").addClass("pane-active").promise().done(function() {
isAnimating = false;
});
});
}
I am wondering if there is an elegant way to remove specific event listeners from a HTML element without affecting similar events.
Ex.
var a = {
addEvent: function(){
$('body').on('click', function(){
//does something here.
});
}
}
var b = {
addEvent: function(){
$('body').on('click', function(){
//does something else here.
});
}
}
a.addEvent();
b.addEvent();
So the question is: how do I remove object a's on click event without removing b's event?
$('body').off('click'); //removes both event listeners
I assume most ways will kill both listeners. It would be great to see any elegant responses.
One option would be to namespace the events:
var a = {
addEvent: function(){
$('body').on('click.a', function(){
//does something here.
});
}
}
var b = {
addEvent: function(){
$('body').on('click.b', function(){
//does something here.
});
}
}
a.addEvent();
b.addEvent();
$('body').off('click.a'); //removes the a event listener
Great answer Josh (and I will give you the points).
The below does not work (I made a bad assumption to believe this would work)... striking through so no one uses this answer. Thanks #Guffa.
I found another solution to this issue worth sharing:
var x = $('body').on('click', function(){
//does something here
});
var y = $('body').on('click', function(){
//does something else here
});
x.off('click'); //this does not remove the event handler assigned to y
(I did not write the code inside the original objects for clarity)
I'm having trouble with my function. I need a for loop to attach a click event to my elements. Now it sadly only adds the click event to the last element.
I did it like this right now:
var $texte = $('.text-bg');
$texte = $texte.children();
for(i=0;i<$texte.length;i++){
switch($($texte[i]).attr('class')){
case 'text-top-left':
console.log($texte[i]);
$($texte[i]).on('click', function(){
$($texte[i]).toggle(
function(){$($texte[i]).animate({'left':'0'})},
function(){$($texte[i]).animate({'left':'-100px'})}
);
});
break;
}
}
But somehow the click event doesnt save. So when it goes into the second loop in the for function, the first click event is being overwritten. How to prevent this?
You can use this inside the event handler to refer to the current element
$($clicker[i]).on('click', function () {
$(this).animate({
'left': '0'
});
});
Why is your solution not working
JavaScript closure inside loops – simple practical example
Creating closures in loops: A common mistake
The given code can be changed as(there is no need to has a switch statement here)
var $texte = $('.text-bg');
$texte.children('.text-top-left').click(function () {
var $this = $(this),
flag = $this.data('animToggle');
$this.data('animToggle', !flag);
$this.animate({
'left': flag ? '0' : '-100px'
})
})
Demo: Fiddle
You need to use this, In event handler it refers to element which executed the event.
Use
$($clicker[i]).on('click', function(){
$(this).animate({'left':'0'}); //Used this here
});
Your code can be simplified as However .toggle(function, function, ... ) removed in jQuery 1.9
$('.text-bg > .text-top-left').on('click', function() {
var toggle = $(this).data('toggle');
$(this).data('toggle', !toggle);
$(this).animate({
'left': toggle ? '-100px' : '0'
});
});
You should be able to attach the handler to all jQuery result elements at once:
$clicker.on('click', function(){
$(this).animate({'left':'0'}); //Used this here
});
With your updated code, you should use the selection facilities already provided by jQuery:
$('.text-bg > .text-top-left').on('click', function() {
$(this).toggle(
function() {
$(this).animate( {'left':'0'})},
function() {
$(this).animate({'left':'-100px'})
});
});
});
To add a second case (for another class) just 'double' the code with the alternative behavior.
You can use
$($texte[i]).on('click', function(){
$(this).animate({'left':'0'});
});
OR simply assign the loop variable to a local variable INSIDE the loop:
var j = i;
$($texte[j]).on('click', function(){
$($texte[j]).animate({'left':'0'});
});
I am looking for a way to manage the events. I have a hover function for element A, and click function for element B. I want to disable A`s hover function temporary while the second click of B.
I am looking for a way that not necessary to rewrite the hole function of A inside of B. Something very simply just like "Store and Disable Event, Call Stored Function"
I found some technique like .data('events') and console.log. I tired but failed, or maybe I wrote them in a wrong way.
Please help and advice!
$(A).hover();
$(b).click(
if($.hasData($(A)[0])){ // if A has event,
//STORE all the event A has, and disable
}else{
//ENABLE the stored event for A
}
);
Try this
var hoverme = function() {
alert('Hover Event Fired');
};
$('.A').hover(hoverme);
var i = 0;
$('.B').on('click', function(){
if(i%2 === 0){
// Unbind event
$('.A').off('hover');
}
else{
// Else bind the event
$('.A').hover(hoverme);
}
i++;
});
Check Fiddle
I think that what you want to do is something like this (example for JQuery 1.7.2):
$("#a").hover(function(){alert("test")});
$("#a")[0].active=true;
$("#b").click(function(){
if($("#a")[0].active){
$("#a")[0].storedEvents = [];
var hoverEvents = $("#a").data("events").mouseover;
jQuery.each(hoverEvents , function(key,handlerObj) {
$("#a")[0].storedEvents.push(handlerObj.handler);
});
$("#a").off('hover');
}else{
for(var i=0;i<$("#a")[0].storedEvents.length;i++){
$("#a").hover($("#a")[0].storedEvents[i]);
}
}
$("#a")[0].active = ($("#a")[0].active)==false;
});
JSFiddle Example
But there are a couple of things that you must have in consideration:
This will only work if you add the events with JQuery, because JQuery keeps an internal track of the event handlers that have been added.
Each version of JQuery handles data("events") differently, that means that this code may not work with other version of JQuery.
I hope that this helps.
EDIT:
data("events") was an internal undocumented data structure used in JQuery 1.6 and JQUery 1.7, but it has been removed in JQuery 1.8. So in JQuery 1.8 the only way to access the events data is through: $._data(element, "events"). But keep in mind the advice from the JQuery documentation: this is not a supported public interface; the actual data structures may change incompatibly from version to version.
You could try having a variable that is outside the scope of functions a and b, and use that variable to trigger the action to take in function b on function a.
var state;
var a = function() {
if(!state) {
state = true;
// Add hover action and other prep. I'd create a third function to handle this.
console.log(state);
};
var b = function() {
if(state) {
state = false;
// Do unbinding of hover code with third function.
} else {
state = true;
// Do whatever else you needed to do
}
}
Without knowing more about what you're trying to do, I'd try something similar to this.
It sounds like you want to disable the click hover event for A if B is clicked.
$("body").on("hover", "#a", function(){
alert("hovering");
});
$("#b").click( function(){
$("body").off("hover", "#a", function() {
alert("removed hovering");
});
});
You can use the jQuery off method, have a look at this fiddle. http://jsfiddle.net/nKLwK/1/
Define a function to assign to hover on A element, so in b click, call unbind('hover') for A element and in second click on b element define again a function to hover, like this:
function aHover(eventObject) {
// Todo when the mouse enter object. You can use $(this) here
}
function aHoverOut(eventObject) {
// Todo when the mouse leave the object. You can use $(this) here
}
$(A).hover(aHover, aHoverOut);
// ...
$(b).click(function(eventObject) {
if($.hasData($(A)[0])){ // if A has event,
$(A).unbind('mouseenter mouseleave'); // This is because not a event hover, jQuery convert the element.hover(hoverIn, hoverOut) in element.bind('mouseenter', hoverIn) and element.bind('mouseleave', hoverOut)
}else{
$(A).hover(aHover, aHoverOut);
}
});
There are provably better ways to do it, but this works fine, on document ready do this:
$("#a")[0].active=false;
$("#b").click(function(){
$("#a")[0].active = ($("#a")[0].active)==false;
if($("#a")[0].active){
$("#a").hover(function(){alert("test")});
}else{
$("#a").off('hover');
}
});
JSFiddle example
You can use .off function from jQuery to unbind the hover on your "a" element.
function hoverA() {
alert('I\'m on hover');
}
$('#a').hover( hoverA );
var active = true;
$('#b').on('click', function(){
if(active){
$('#a').off('hover');
active = false;
} else{
$('#a').hover(hoverA);
active = true;
}
});
Live demo available here : http://codepen.io/joe/pen/wblpC
I have an anchor tag on my page, I want an event attached to it, which will fire when the display of this element change.
How can I write this event, and catch whenever the display of this element changes?
This is my way of doing on onShow, as a jQuery plugin. It may or may not perform exactly what you are doing, however.
(function($){
$.fn.extend({
onShow: function(callback, unbind){
return this.each(function(){
var _this = this;
var bindopt = (unbind==undefined)?true:unbind;
if($.isFunction(callback)){
if($(_this).is(':hidden')){
var checkVis = function(){
if($(_this).is(':visible')){
callback.call(_this);
if(bindopt){
$('body').unbind('click keyup keydown', checkVis);
}
}
}
$('body').bind('click keyup keydown', checkVis);
}
else{
callback.call(_this);
}
}
});
}
});
})(jQuery);
You can call this inside the $(document).ready() function and use a callback to fire when the element is shown, as so.
$(document).ready(function(){
$('#myelement').onShow(function(){
alert('this element is now shown');
});
});
It works by binding a click, keyup, and keydown event to the body to check if the element is shown, because these events are most likely to cause an element to be shown and are very frequently performed by the user. This may not be extremely elegant but gets the job done. Also, once the element is shown, these events are unbinded from the body as to not keep firing and slowing down performance.
You can't get an onshow event directly in JavaScript. Do remember that the following methods are non-standard.
IN IE you can use
onpropertychange event
Fires after the property of an element
changes
and for Mozilla
you can use
watch
Watches for a property to be assigned
a value and runs a function when that
occurs.
You could also override jQuery's default show method:
var orgShow = $.fn.show;
$.fn.show = function()
{
$(this).trigger( 'myOnShowEvent' );
orgShow.apply( this, arguments );
return this;
}
Now just bind your code to the event:
$('#foo').bind( "myOnShowEvent", function()
{
console.log( "SHOWN!" )
});
The code from this link worked for me: http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/
(function ($) {
$.each(['show', 'hide'], function (i, ev) {
var el = $.fn[ev];
$.fn[ev] = function () {
this.trigger(ev);
return el.apply(this, arguments);
};
});
})(jQuery);
$('#foo').on('show', function() {
console.log('#foo is now visible');
});
$('#foo').on('hide', function() {
console.log('#foo is hidden');
});
However the callback function gets called first and then the element is shown/hidden. So if you have some operation related to the same selector and it needs to be done after being shown or hidden, the temporary fix is to add a timeout for few milliseconds.