Remove click event handler immediately after click - javascript

I've been trying to remove the click event handler after being clicked once to prevent the event from firing multiple times.
I've tried .unbind(), .off(), .one() and put each in various different places in the function, but none seem to work.
If I use .off() I get a undefined is not a function error.
If I use .one() I want to basically turn the event back on if I click the other button. So if I click on select, I want it to only fire once, but if I then click on unselect, I want select to be clickable again.
I've tried doing the following, but oddly the code doesn't even fire, and so I've been forced to use click().
$().on('click', function()...
Which function should I use and where should I put it?
(function($) {
Drupal.behaviors.wysiwyg = {
attach: function(context, settings) {
var toggle = function(state) {
$('#wysiwyg-profile-form .buttons-and-plugins input:checkbox').attr('checked', state);
}
$('#wysiwyg-select-all').click(function(){
toggle(true);
return false;
});
$('#wysiwyg-unselect-all').click(function(){
toggle(false);
return false;
});
}
}
}(jQuery))

Check if this works for you. Also make sure the jquery version is above 1.7 for "on", "off" to work.
(function($) {
Drupal.behaviors.wysiwyg = {
attach: function(context, settings) {
var counter = 0;
var toggle = function(state) {
$('#wysiwyg-profile-form .buttons-and-plugins input:checkbox').attr('checked', state);
}
$(document).off('click',"#wysiwyg-select-all").on('click', "#wysiwyg-select-all", function(e) {
toggle(true);
return false;
});
$(document).off('click',"#wysiwyg-unselect-all").on('click', "#wysiwyg-unselect-all", function(e) {
toggle(false);
return false;
});
}
}
}(jQuery))

UPDATED
I was messing around with a couple of the things these other guys were suggesting and I think this is exactly what you need to accomplish this:
(function ($) {
Drupal.behaviors.wysiwyg = {
attach: function (context, settings) {
var selectFn = function() {
toggle(true);
return false;
}
var unselectFn = function() {
toggle(false);
return false;
}
var toggle = function (state) {
$('#wysiwyg-profile-form .buttons-and-plugins input:checkbox').attr('checked', state);
if (state) {
$(document).off("click", '#wysiwyg-select-all,#wysiwyg-unselect-all', "**");
$(document).on("click", "#wysiwyg-unselect-all", unselectFn;
}else{
$(document).off("click", '#wysiwyg-select-all,#wysiwyg-unselect-all', "**");
$(document).on("click", "#wysiwyg-select-all", selectFn;
}
};
$(document).on("click", "#wysiwyg-select-all", selectFn);
$(document).on("click", "#wysiwyg-unselect-all", unselectFn);
}
};
}(jQuery));
It toggles the binding of the buttons events and will work with dynamically created elements. This is a derivative of Matt Green's answer, but I noticed several problems with his, namely that he was calling the selectFn and unselectFn in his event binding rather than referencing them.

Something like this:
$("#wysiwyg-select-all").one("click", function() {
toggle(true);
return false;
});

Add a class name to the click button and then remove the class once its clicked once.
$('#wysiwyg-select-all .myclassname').on('click', function(){
toggle(true);
$(this).removeClass('myclassname');
$('#wysiwyg-unselect-all').addClass('secondclassname');
return false;
});
$('#wysiwyg-unselect-all .secondclassname').on('click', function(){
toggle(false);
$('#wysiwyg-select-all').addClass('myclassname');
$(this).removeClass('secondclassname');
return false;
});

Why don't you use any global variable as a flag. On click event set it true.
if (btn1Clicked)
return;
btn1Clicked=true;
And to reenable just set it false.
You can use return false instead of return if required.

This should work for dynamically created elements:
(function ($) {
Drupal.behaviors.wysiwyg = {
attach: function (context, settings) {
var toggle = function (state) {
$('#wysiwyg-profile-form .buttons-and-plugins input:checkbox').attr('checked', state);
};
$(document).on("click", "#wysiwyg-select-all", selectFn());
$(document).on("click", "#wysiwyg-unselect-all", unselectFn());
}
};
}(jQuery));
function selectFn() {
$(document).off("click", '#wysiwyg-select-all', "**");
$(document).on("click", "#wysiwyg-unselect-all", unselectFn());
toggle(true);
return false;
}
function unselectFn() {
$(document).off("click", '#wysiwyg-unselect-all', "**");
$(document).on("click", "#wysiwyg-select-all", selectFn());
toggle(false);
return false;
}

Related

Copy click events, destroy them and attach them later

I'm trying to completely disable the click events for some elements but to have the option to reenable them later.
What I have try so far:
$('a').on('click', function(e) {
// some stuffs...
});
...
$('a').each(function(i, el) {
this.clickEvents = $._data(el, 'events').click;
$(el).off('click');
});
// reenable them later (this is not working)
$('a').each(function(i, el) {
$(el).on('click', this.clickEvents);
});
...
Any ideas where I'm wrong?
Try .handler in each clickEvent:
$('a').each(function(i, el) {
// clickEventHandlers is an array of handler functions
this.clickEventHandlers = $._data(el, 'events').click.map(function(e) {
return e.handler;
});
$(el).off('click');
});
// re-enable them later
$('a').each(function(i, el) {
// reapply the handlers in order
this.clickEventHandlers.forEach(function(handler) {
$(el).on('click', handler);
});
});
The great thing about this is that you re-apply all event listeners in order.
Working JSFiddle.
Try $._data(el, 'events').click[0].handler for first click event , or use $.each() to iterate all click or other events ; store handler at element .data() ; e.g., .data("events") ; reattach handler using $(this).data().events[0]
$("a").data("events", []).on("click", function() {
console.log(this)
});
$("a").each(function(i, el) {
$.each($._data(el, 'events').click, function(index, event) {
$(el).data().events.push(event.handler)
})
console.log($(this).data("events"));
$(el).off("click");
});
// reenable them later (this is not working)
$("a").each(function(i, el) {
$.each($(this).data().events, function(index, handler) {
$(el).on("click", handler);
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
click

Need help in using .on in jquery so that it can work with dynamic data also

I have a function "single_double_click" and I am invoking the same via $('#packagelisttable tr').single_double_click(fn), which works fine with static data.
However it is not responding when I am deploying the same application to work with dynamic data.
I tried using .on also as mentioned in several posts but then also no success.Please find the same below:
$(#packagelisttable ).on('single_double_click', 'tr', fn)
$(document).on('single_double_click', 'packagelisttable tr', fn)
I need to click on a row of table (#packagelisttable) and need to check whether it was a single or double click.
Please find the code which I am using:
jQuery.fn.single_double_click = function (single_click_callback, double_click_callback, timeout) {
return this.each(function () {
var clicks = 0,
self = this;
jQuery(this).click(function (event) {
clicks++;
if (clicks == 1) {
setTimeout(function () {
if (clicks == 1) {
single_click_callback.call(self, event);
} else {
double_click_callback.call(self, event);
}
clicks = 0;
}, timeout || 300);
}
});
});
}
//$(#packagelisttable ).on('single_double_click', 'tr', function(){
//$(document).on('single_double_click', 'packagelisttable tr', function(){
// $('#packagelisttable tr').single_double_click(function () {
alert("Try double-clicking me!")
},
function () {
alert("Double click detected")
});
The delegated event version of on is used for events, but single_double_click is not an event. It is a function.
It is not possible to connect a jQuery plugin/function to a dynamically loaded elements that way.
You either need to connect any new elements to your plugin after load, or change the plugin to use classes (e.g. class="singleordouble") and use a delegated click event handler, or you can add a selector as an additional parameter and attach to a non-changing ancestor element (as Cerlin Boss demonstrates).
e.g.
jQuery(document).on('click', '.singleordouble', function (event) {
But if you do that, using a plugin becomes pointless.
It is more flexible to generate your own custom click events, using the settimeout trick you already have.
Here is a full example using custom events: http://jsfiddle.net/TrueBlueAussie/wjf829ap/2/
Run this code once anywhere:
// Listen for any clicks on the desired
$(document).on('click', '.singleordouble', function (e) {
var $this = $(this);
var clicks = $this.data("clicks") || 0;
// increment click counter (from data stored against this element)
$(this).data("clicks", ++clicks);
// if we are on the first click, wait for a possible second click
if (clicks == 1) {
setTimeout(function () {
var clicks = $this.data("clicks");
if (clicks == 1) {
$this.trigger("customsingleclick");
} else {
$this.trigger("customdoubleclick");
}
$this.data("clicks", 0);
}, 300);
}
});
It will generate custom events (called customsingleclick and `customdoubleclick in this example, but call them whatever you want).
You can then simply listen for these custom events:
$(document).on('customsingleclick', function(e){
console.log("single click on " + e.target.id);
});
$(document).on('customdoubleclick', function(e){
console.log("double click on " + e.target.id);
});
Or using delegated event handlers: http://jsfiddle.net/TrueBlueAussie/wjf829ap/3/
$(document).on('customsingleclick', '.singleordouble', function(){
console.log("single click on " + this.id);
});
$(document).on('customdoubleclick', '.singleordouble', function(){
console.log("double click on " + this.id);
});
how about this
I have made some small changes to your code. Not sure if this will work for you.
I have added one more parameter which takes a selector. Please comment if you have any doubt.
jQuery.fn.single_double_click = function (selector, single_click_callback, double_click_callback, timeout) {
return this.each(function () {
var clicks = 0,
self = this;
jQuery(this).on('click', selector, function (event) {
clicks++;
if (clicks == 1) {
setTimeout(function () {
if (clicks == 1) {
single_click_callback.call(self, event);
} else {
double_click_callback.call(self, event);
}
clicks = 0;
}, timeout || 300);
}
});
});
}
Usage :
$('#headmnu').single_double_click('li',
function () {
; // not doing anything here
}, function () {
alert('twice')
});
Here li is the child of the first jquery selector($('#headmnu')) which is a ul
This will work with dynamically added elements also.
UPDATE
Just to clarify $('#headmnu') is a parent element of all lis.
I have used event delegation here to achieve this. Please refer the documentation for more info
I checked your code and if you have pasted, then you should also check
$(#packagelisttable ).on('single_double_click', 'tr', fn) // shold be
$('#packagelisttable').on('single_double_click', 'tr', fn)
$(document).on('single_double_click', 'packagelisttable tr', fn) // should be
$(document).on('single_double_click', '#packagelisttable tr', fn)

How to close div when div loses focus?

I made a simple plunkr here http://plnkr.co/edit/zNb65ErYH5HXgAQPOSM0?p=preview
I created a little datepicker I would like this to close itself when you focus out of it (focusout of datepicker) if I put blur on input I'm unable to use the datepicker, if I put focusout event on datepicker it doesn't works
I also tried:
angular.element(theCalendar).bind('blur', function () {
$scope.hideCalendar();
});
but it doesn't work.
Any clue?
this is because you are removing the item before you get a chance to do anything, here is a working example:
http://plnkr.co/edit/mDfV9NLAQCP4l7wHdlfi?p=preview
just add a timeout:
thisInput.bind('blur', function () {
$timeout(function(){
$scope.hideCalendar();
}, 200);
});
have you considered using existing datepickers? like angularUI or angular-strap: http://mgcrea.github.io/angular-strap/##datepickers
Update:
Not a complete solution, but should get you quite closer:
angular.element($document[0].body).bind('click', function(e){
console.log(angular.element(e.target), e.target.nodeName)
var classNamed = angular.element(e.target).attr('class');
var inThing = (classNamed.indexOf('datepicker-calendar') > -1);
if (inThing || e.target.nodeName === "INPUT") {
console.log('in');
} else {
console.log('out');
$timeout(function(){
$scope.hideCalendar();
}, 200);
}
});
http://plnkr.co/edit/EbQl5xsCnG837rAEhBZh?p=preview
What you want to do then is to listen for a click on the page, and if the click is outside of the calendar, then close it, otherwise do nothing. The above only takes into account that you are clicking on something that has a class name which includes datepicker-calendar, you will need to adjust it so that clicking within the calendar doesn't close it as well.
How about closing on mouseout?
You need to cancel the close if you move to another div in the calendar though:
//get the calendar as element
theCalendar = element[0].children[1];
// hide the calendar on mouseout
var closeCalendarTimeout = null;
angular.element(theCalendar).bind('mouseout', function () {
if ( closeCalendarTimeout !== null )
$timeout.cancel(closeCalendarTimeout);
closeCalendarTimeout = $timeout(function () {
$scope.hideCalendar();
},250)
});
angular.element(theCalendar).bind('mouseover', function () {
if ( closeCalendarTimeout === null ) return
$timeout.cancel(closeCalendarTimeout);
closeCalendarTimeout = null;
});
EDIT
Adding a tabindex attribute to a div causes it to fire focus and blur events.
, htmlTemplate = '<div class="datepicker-calendar" tabindex="0">' +
angular.element(theCalendar).bind('blur', function () {
$scope.hideCalendar();
});
So, i know it probably is not the best practice or the best way to do this, but at the end i fixed and got what i need using this:
thisInput.bind('focus click', function bindingFunction() {
isMouseOnInput = true;
$scope.showCalendar();
angular.element(theCalendar).triggerHandler('focus');
});
thisInput.bind('blur focusout', function bindingFunction() {
isMouseOnInput = false;
});
angular.element(theCalendar).bind('mouseenter', function () {
isMouseOn = true;
});
angular.element(theCalendar).bind('mouseleave', function () {
isMouseOn = false;
});
angular.element($window).bind('click', function () {
if (!isMouseOn && !isMouseOnInput) {
$scope.hideCalendar();
}
});
I setted up some boolean vars to check where mouse is when you click the page and it works like a charm if you have some better solution that works , please let me know, but this actually fixed all.
I accept this as the answer but i thank all the guys on this page!

Combining jquery functions - on() hover/mouseenter/mouseleave

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

Wrap existing jquery event with defaults

I'm trying to figure out how I can wrap the click event. Eg this is the code I want.
$("#test").aclick(function() {
alert('hi');
});
The only thing that aclick does is use the e.preventDefault automatically.
Is it something like?
$.fn.aclick = function(e) {
e.preventDefault();
return $.fn.click.apply(this, arguments);
});
Thanks.
When binding a handler, the event object e doesn't exist yet. You need to create a default event handler that calls the supplied one:
$.fn.aclick = function (handler) {
return this.each(function () {
var el = this;
$(el).click(function (e) {
e.preventDefault();
handler.call(el, e);
});
});
};

Categories