Copy click events, destroy them and attach them later - javascript

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

Related

Why does my jQuery delegated click fire multiple times?

I am copying an element and adding it to a a list of elements. First, I get some HTML using an ajax call:
var setButtonClick = function (url, btn) {
$.ajax(url, $('form').serialize(), 'Html').then(function (data) {
$(btn).parent().find(sel.addListItem).on('click', function () {
addListItem(data, this, btn);
});
addListItem(data, btn, btn);
});
}
addListItem looks like this:
var addListItem = function (data, context, btn) {
var $template = $(data);
// stuff not related removed for brevity
$(btn).before($template);
}
I then have a remove function using a delegate:
$(sel.editableList).delegate(sel.removeListItem, 'click', function () {
// fires once for every element with the sel.removeListItem selector
}
I need the click event to fire once for the clicked element only. I can get a basic version of delegate working by inserting content like this:
$( "body" ).delegate( "p", "click", function() {
$( this ).after( "<p>Another paragraph!</p>" );
});
Therefore, I'm thinking it may be because I'm inserting a copy of the element or is it the same element I'm adding over and over? I've also tried to use clone to create a new element before inserting like:
var $template = $(data).clone();
Can anyone show me where I am going wrong with this please?
The problem is that every time your ajax is called you attach a click event handler to the elements. It gets called repeatedly, because you add it to the elements that already existed and had this handler attached.
The solution for your problem is to detach previously attached handlers with off() function.
var setButtonClick = function (url, btn) {
$.ajax(url, $('form').serialize(), 'Html').then(function (data) {
$(btn).parent().find(sel.addListItem)
.off('click')
.on('click', function () {
addListItem(data, this, btn);
});
addListItem(data, btn, btn);
});
}
#
In the future you may want to attach different click event handlers or may want to turn off specific handlers, for that you could use namespaces.
$(elem).on('event.namespace', function(){});
$(elem).off('event.namespace');
That way you could have multiple click event handlers on one element. This would be the code if you have more than one click event handlers
var setButtonClick = function (url, btn) {
$.ajax(url, $('form').serialize(), 'Html').then(function (data) {
$(btn).parent().find(sel.addListItem)
.off('click.addItem')
.on('click.addItem', function () {
addListItem(data, this, btn);
});
addListItem(data, btn, btn);
});
}
#
And here's the example.
$('.btn').on('click.ns1', function(){
alert('Hey');
});
$('.btn').on('click.ns2', function(){
alert('How you doin?');
});
// Comment me out to see the difference
$('.btn').off('click.ns2');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="btn">Click me</button>

How to "Hijack" jQuery events and then trigger the default handler on some condition

I am trying to do something like Hijack the jQuery events of some elements, do a judgement, and then trigger the default handler if necessary.
Say I have a page with some inputs and buttons, and each of them are already bind with event handlers(click, focu key events, etc.) to do their jobs.
What I want to achieve is: whenever these elements' events are fired, I'd like to trigger my function first, this function will gather some data, and judge whether the execution should continue.
If no the corresponding handler should not be called like nothing happened.
If yes the original handler should be triggered with the original event data.
I don't want to change the default handlers to achieve this, so, is there a good way to do something like this?
Thanks
You could use the internal method _data()
/* add some event handlers */
$("button").on("click", function() {
console.log("Button clicked");
});
$("button").on("click", function() {
console.log("Button clicked 2");
});
/* hijack them */
var button = $("button"),
boundEvents = $._data(button.get(0)).events;
$.each(boundEvents, function(key, eventHandlers) {
// save the handlers from being "destroyed" by .off()
eventHandlers = eventHandlers.map(function(eh) {
return eh.handler;
});
// remove the event handlers
button.off(key);
// add the hijacked version
$.each(eventHandlers, function(idx, handler) {
button.on(key, function(e) {
if ($("#cb").prop("checked")) {
handler(e);
} else {
console.log("nothing to do...");
}
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="checkbox" id="cb" />
<br />
<button>Click me</button>
Try defining function at .data() of each that has events attached which is called at if condition within handler. If function returns true , do stuff , if function returns false do not do stuff
$("div").on("click", function() {
// if `check` returns `true`, do stuff
if ($(this).data().check()) {
console.log(this.tagName)
}
});
$("input").on("focus", function() {
// if `check` returns `true`, do stuff
if ($(this).data().check()) {
console.log(this.tagName)
}
});
var elems = $("*").filter(function(i,el) {
return $._data(el).events !== undefined
})
elems.each(function(i, el) {
$(el).data().check = function() {
// define condition here
// if element has `css` `color` property set to `"blue"`
// return `true`, else return `false`
if ($(el).css("color") === "rgb(0, 0, 255)") return true
else return false
}
});
div {
color:blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div>click</div>
<input type="text" />
<span>text</span>

Merging event listeners

I have these event listeners:
$(document).on("pagebeforeshow",function(e,ui){
// do something
});
$(document).on("click","[data-json]",function(){
// do something else
});
I can merge them like so:
$(document).on({
pagebeforeshow: function(e, ui) {
// do something here
},
click: function(e) {
// do something else here
}
});
... but I'm not sure how to bind the click to [data-json] as in the first example.
You could use this line
click: function(e) {
if(!$(e.target).is($("[data-json]"))) return;
}
it will simulate what you first example was doing. It only runs the function if element that is firing has attribute data-json

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)

Remove click event handler immediately after click

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;
}

Categories