I have the following event:
$(document).on('scroll', function() {
// do stuff
});
At the completion of this event, I want to disconnect it. I can do $(document).off('scroll');, but that will disconnect all the scroll events (there are several) on the page.
How do I disconnect just this one event?
How do I disconnect just this one event?
by .namespacing it!
$(document).on({
"scroll.myNamespace" : function() {
// do stuff
}
});
than at some point you can either
$(document).off("scroll.myNamespace");
or all your namespaced events (if you have more of them, like i.e: scroll.myNamespace, click.myNamespace etc...)
$(document).off(".myNamespace");
Example: how to off a namespaced event
$(document).on({
"scroll.teapot": function() {
console.log("TEAPOT!")
},
scroll: function() {
console.log("scroll")
}
});
$("#offTeapot").on({
click: function() {
$(document).off(".teapot"); // off every ".teapot" namespaced event
}
})
body {
height: 400vh;
}
button {
position: fixed;
top: 0;
left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="offTeapot">OFF TEAPOTS</button>
<h1>SCROLL!!!!!</h1>
In order to remove a specific handler using off(), you'll need to pass a reference to the handler, so simply declare your handler function and pass the reference to both on() and off():
function scrollHandler() {
// do stuff
}
$(document).on('scroll', scrollHandler);
$(document).off('scroll', scrollHandler);
The .off() method removes event handlers that were attached with .on(). See the discussion of delegated and directly bound events on that page for more information. Calling .off() with no arguments removes all handlers attached to the elements. Specific event handlers can be removed on elements by providing combinations of event names, namespaces, selectors, or handler function names.
http://api.jquery.com/off/
You can separate the call back and put it into its own function, then you can remove it.
function doScroll(){
// Do stuff
}
// Add the function
$(document).on('scroll', doScroll);
// Remove the function
$(document).off('scroll', doScroll);
Did you try setting it specifically?
$(document).on('scroll', '#yourId', function(){
}).off('scroll', '#yourId', function(){
});
How about setup a global flag for one time usage:
var doThis = true;
$(document).on('scroll', function() {
if(doThis){
// do stuff
doThis = false;
}
});
Related
Finding myself in a bit of a strange position where I have to reference the tooltip within an instantiation for all tooltips.
$('body').tooltip({
selector: '[data-toggle="tooltip"]',
html: true,
animation: false,
}).on("mouseenter", function (e) {
e.stopPropagation();
var _this = e.target;
$(_this).tooltip("show");
$(".tooltip").on("mouseleave", function () {
$(_this).tooltip('hide');
});
}).on("mouseleave", function (e) {
e.stopPropagation();
var _this = e.target;
setTimeout(function () {
if (!$(".tooltip:hover").length) {
$(_this).tooltip("hide");
}
}, 300);
});
That being said, how can I:
Reference the actual element that is triggering this jQuery call
Keep the tooltip open while either the actual tooltip or element that generated it are being hovered over?
Here is a link to a JSFiddle prototype:
https://jsfiddle.net/cwv57weu/8/
Within your '.on()' call, you can add an 'event' argument to your anonymous function. This will contain all of the data from the event including the element which triggered the event (it will be referenced as 'target').
}).on("mouseenter", function (event) {
$(event.target).tooltip("show");
})
the event argument contains a ton of data, I would play around with it by console.log(event) within your anonymous function to get a feel as to what data is available to you.
Use event.target.
$('body').tooltip({
selector: '[data-toggle="tooltip"]',
html: true,
animation: false,
}).on("mouseenter", function (e) {
var _this = e.target;
$(_this).tooltip("show");
$(".tooltip").one("mouseleave", function () {
$(_this).tooltip('hide');
});
}).on("mouseleave", function (e) {
var _this = e.target;
setTimeout(function () {
if (!$(".tooltip:hover").length) {
$(_this).tooltip("hide");
}
}, 300);
});
e.target is the actual element that the event originated on, while this is the element that the event listener was attached to (equivalent to e.currentTarget).
Note that because of event bubbling, the event will fire on all the containing elements up to body. You may want to use e.stopPropagation() to prevent bubbling, so you only process the deepest element.
I also changed the mouseleave handler on .tooltip to use .one(). Otherwise, every time you enter something, you'll add another mouseleave handler to all the tooltips, without removing the previous one, and soon there will be thousands of handlers running (this is why it's generally wrong to bind event handlers inside other event handlers). I'm not really sure you need both that mouseleave handler and the one you attach to body.
$('#clickableElement').bind({
mousedown: function(e)
{
console.log('mousedown on element');
$(document).bind('mouseup',function(e){
console.log('mouseup caught');
//Do some magic here
$(this).unbind('mouseup');
});
},
mouseup:function(e)
{
//mouseup within element, no use here.
}
});
I'm trying to catch the mouseup event from a mousedown that's released inside or outside of an element. This code almost works, but the problem is the unbind('mouseup') which is unbinding other scripts attached to the mouseup event (jqueryui). If unbind() is not set then the code gets stacked within mouseup event and called x number of times, where x is the times you've mousedowned.
Route 1: is there some kind of self destructing function that calls itself once and destroys?
Route 2: any way to copy/clone the mouseup function prior to inserting the code, then unbind, then set as previous?
Ideally I'd like to keep this code structure for neatness as I have lots of clickable elements, so binding the document.mouseup outside of element.mousedown would be messy.
Here's the Fiddle I forgot to add http://jsfiddle.net/9gFNk/
Can giv your click event a namespace so only that namespaced event gets unbound, and not any others
$(document).on('mouseup.clickableElement',function(e){
console.log('mouseup caught');
//Do some magic here
$(this).off('mouseup.clickableElement');
});
I created a global object to catch mouse events from the document. It's currently set for mouseup only but can be easily expanded for others. The mouseup code is still customizable within the mousedown functions of the clickable elements so it this handy if you have lots of clickable things like I do.
var MouseCatcher=function()
{
this.init=function()
{
var mc = this;
$(document).bind({
mouseup:function(e)
{
mc.mouseup();
}
});
}
this.mouseup=function()
{
return false;
}
}
var mouseCatcher = new MouseCatcher();
mouseCatcher.init();
$('#clickableElement').bind({
mousedown: function(e)
{
console.log('mousedown on element');
mouseCatcher.mouseup=function()
{
console.log('mouseup called from MouseCatcher');
this.mouseup = function(){return false;}
}
},
mouseup:function(e)
{
//mouseup within element, no use here.
}
});
With "on" event its possible, its may not be an exact solution. Please refer this code
$(document).on('mousedown', function() {
$('#clickableElement').css('display', 'none');
$(document).bind('mouseup', function() {
$('#clickableElement').css('display', 'block');
});
});
http://jsfiddle.net/9gFNk/13/
I have a bunch of elements that get three different classes: neutral, markedV and markedX. When a user clicks one of these elements, the classes toggle once: neutral -> markedV -> markedX -> neutral. Every click will switch the class and execute a function.
$(document).ready(function(){
$(".neutral").click(function markV(event) {
alert("Good!");
$(this).addClass("markedV").removeClass("neutral");
$(this).unbind("click");
$(this).click(markX(event));
});
$(".markedV").click(function markX(event) {
alert("Bad!");
$(this).addClass("markedX").removeClass("markedV");
$(this).unbind("click");
$(this).click(neutral(event));
});
$(".markedX").click(function neutral(event) {
alert("Ok!");
$(this).addClass("neutral").removeClass("markedX");
$(this).unbind("click");
$(this).click(markV(event));
});
});
But obviously this doesn't work. I think I have three obstacles:
How to properly bind the changing element to the already defined function, sometimes before it's actually defined?
How to make sure to pass the event to the newly bound function [I guess it's NOT accomplished by sending 'event' to the function like in markX(event)]
The whole thing looks repetitive, the only thing that's changing is the alert action (Though each function will act differently, not necessarily alert). Is there a more elegant solution to this?
There's no need to constantly bind and unbind the event handler.
You should have one handler for all these options:
$(document).ready(function() {
var classes = ['neutral', 'markedV', 'markedX'],
methods = {
neutral: function (e) { alert('Good!') },
markedV: function (e) { alert('Bad!') },
markedX: function (e) { alert('Ok!') },
};
$( '.' + classes.join(',.') ).click(function (e) {
var $this = $(this);
$.each(classes, function (i, v) {
if ( $this.hasClass(v) ) {
methods[v].call(this, e);
$this.removeClass(v).addClass( classes[i + 1] || classes[0] );
return false;
}
});
});
});
Here's the fiddle: http://jsfiddle.net/m3CyX/
For such cases you need to attach the event to a higher parent and Delegate the event .
Remember that events are attached to the Elements and not to the classes.
Try this approach
$(document).ready(function () {
$(document).on('click', function (e) {
var $target = e.target;
if ($target.hasClass('markedV')) {
alert("Good!");
$target.addClass("markedV").removeClass("neutral");
} else if ($target.hasClass('markedV')) {
alert("Bad!");
$target.addClass("markedX").removeClass("markedV");
} else if ($target.hasClass('markedX')) {
alert("Ok!");
$target.addClass("neutral").removeClass("markedX");
}
});
});
OR as #Bergi Suggested
$(document).ready(function () {
$(document).on('click', 'markedV',function (e) {
alert("Good!");
$(this).addClass("markedV").removeClass("neutral");
});
$(document).on('click', 'markedX',function (e) {
alert("Bad!");
$(this).addClass("markedX").removeClass("markedV");
});
$(document).on('click', 'neutral',function (e) {
alert("Ok!");
$(this).addClass("neutral").removeClass("markedX");
});
});
Here document can be replaced with any static parent container..
How to properly bind the changing element to the already defined function, sometimes before it's actually defined?
You don't bind elements to functions, you bind handler functions to events on elements. You can't use a function before it is defined (yet you might use a function above the location in the code where it was declared - called "hoisting").
How to make sure to pass the event to the newly bound function [I guess it's NOT accomplished by sending 'event' to the function like in markX(event)]
That is what happens implicitly when the handler is called. You only need to pass the function - do not call it! Yet your problem is that you cannot access the named function expressions from outside.
The whole thing looks repetitive, the only thing that's changing is the alert action (Though each function will act differently, not necessarily alert). Is there a more elegant solution to this?
Yes. Use only one handler, and decide dynamically what to do in the current state. Do not steadily bind and unbind handlers. Or use event delegation.
I have a $(document).ready function that sets up listeners for certain elements. However, all of the #leave-ride elements are added dynamically.
Listeners:
$(document).ready(function() {
$("#post-ride").click(function() {
addRide(currentDriver, $(destinationInput).val(), $(originInput).val(), $(dateInput).val(), $(timeInput).val());
$.getScript("scripts/myRides.js", function() {});
});
$("#request-ride").click(function() {
requestRide(currentDriver, $(destinationInput).val(), $(originInput).val(), $(dateInput).val(), $(timeInput).val());
$.getScript("scripts/myRides.js", function() {});
});
$("#leave-ride").click(function() {
console.log("leave Ride");
leaveRide(currentDriver, $("leave-ride").closest("div").attr("id"));
$.getScript("scripts/myRides.js", function() {});
});
});
What do I need to do to get that listener to listen to dynamic content?
Yes, ready runs only once. You can use event delegation:
Take the element closest to the #leave-ride which is not loaded dynamically (document in extreme cases). Then attach the handler on it, and use #leave-ride as the selector for the delegated event.
Assuming a div having the id #container is that static element:
$('div#container').on('click', '#leave-ride', function(){…});
See also Event binding on dynamically created elements?
Use on, change your event declaration
$("#post-ride").click(function() {
to
$("body").on('click',"#post-ride",(function() {
Use .on()
Example:
$("#leave-ride").on('click', function() {
console.log("leave Ride");
leaveRide(currentDriver, $("leave-ride").closest("div").attr("id"));
$.getScript("scripts/myRides.js", function() {
});
});
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.