jQuery events don't work when html is injected - javascript

I have something like this:
function SetTableBehavior() {
$(".displayData tr").hover(function(e) {
$(this).children().addClass('displayDataMouseOver');
}, function () {
$(this).children().removeClass('displayDataMouseOver');
});
$(".displayData tr td").click(function(e) {
var rowsSel = $(".displayData .displayDataRowSelected");
for (var i = 0; i < rowsSel.length; i++) {
var rowSel = rowsSel[i];
$(rowSel).children().removeClass("displayDataRowSelected");
}
$(this).parent().addClass('displayDataRowSelected');
var p = $(this).parent();
p.children().addClass('displayDataRowSelected');
});
}
When the body of the table is injected neither hover or click work.
If i use
$(".displayData tr td").live('click',function(e) {
the click event works but
$(".displayData tr").live('hover',function(e) {
doesn't work
What is the solution so that hover works.
Thanks.
It seems to work like this:
function SetTableBehavior() {
$(".displayData tr").live('mouseenter', function (e) {
$(this).children().addClass('displayDataMouseOver');
}).live('mouseleave', function(e) {
$(this).children().removeClass('displayDataMouseOver');
});
$(".displayData tr td").live('click',function(e) {
var rowsSel = $(".displayData .displayDataRowSelected");
for (var i = 0; i < rowsSel.length; i++) {
var rowSel = rowsSel[i];
$(rowSel).children().removeClass("displayDataRowSelected");
}
$(this).parent().addClass('displayDataRowSelected');
var p = $(this).parent();
p.children().addClass('displayDataRowSelected');
});
}

$(".hoverme").live("mouseover mouseout", function(event) {
if ( event.type == "mouseover" ) {
// do something on mouseover
} else {
// do something on mouseout
}
});
From here: http://api.jquery.com/live/
There is no event called "hover" so you can't use it with live or bind. It is just a "short-cut" that jQuery implemented for us.

You cannot use hover with live. You'll have to split it up in 2 separate event listeners: one for mouseenter, and another one for mouseleave.
Additionally, in your situation, you don't need live. Use delegate, which is much better for performance:
$(".displayData").delegate('tr', 'mouseeneter',function(e) {
$(this).children().addClass('displayDataMouseOver');
})
.delegate('tr', 'mouseleave',function(e) {
$(this).children().removeClass('displayDataMouseOver');
});

hover(a, b) is a shortcut for mouseenter(a).mouseleave(b) (which themselves, are shortcuts for bind('mouseenter', a).bind('mouseleave', b)), so try:
$(".displayData tr").live('mouseenter', function(e) {
// mouseenter handler
}).live('mouseleave', function (e) {
// mouseleave handler
});
For more info, see the hover() and live() docs.

Related

jquery click event working multiple times

I am using jquery to find each target element in iframe on click event. But the click event triggers mutiple times on each click. This is the code that i used. I am using this function to style each target element on click. How can i solve this issue.
var getElement = function () {
$('[data-edit="froala"]').on('froalaEditor.initialized', function (e, editor) {
var $div_tag = $('[data-edit="froala"]').find('iframe').contents().find('body');
$div_tag.on('click', function(e) {
var element_name = e.target.nodeName.toLowerCase();
var $target_class = $('[data-target="'+element_name+'"]');
trigger_object(e);
});
});
}
var trigger_object = function (e) {
$('body').on('change', '[data-style="hr-style"]', function (event) {
$(e.target).css($(this).data('css'),this.value);
});
$('body').on('change', '[data-style="div-style"]', function (event) {
$(e.target).css($(this).data('css'),this.value);
});
}
unbind your existing change event before binding it to prevent event from working multiple times
add .off('change') to unbind all exiting change event
before .on('change') to bind the change event
$('[data-style="hr-style"]').off('change').on('change', function (event) {
$(e.target).css($(this).data('css'),this.value);
});
$('[data-style="div-style"]').off('change').on('change', function (event) {
$(e.target).css($(this).data('css'),this.value);
});
Then probably foralaEditor.initialized is fired multiple times.
Try with off, but on the div_tag click not on body,
var getElement = function () {
$('[data-edit="froala"]').on('froalaEditor.initialized', function (e, editor) {
var $div_tag = $('[data-edit="froala"]').find('iframe')contents().find('body');
$div_tag.off('click').on('click', function(e) {
var element_name = e.target.nodeName.toLowerCase();
var $target_class = $('[data-target="'+element_name+'"]');
trigger_object(e);
});
});
}

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)

Adding bulk items to DOM while preserving jQuery event handling?

I'm adding some bulk <li> elements to a <ul>.
Previously, I was doing something like:
var playlistItemListUl = $('#PlaylistItemList ul');
for(var i = 0; i < 1000; i++ ){
var listItem = $('<li/>', {
'data-itemid': item.get('id'),
contextmenu: function (event) {
console.log('contextmenu', event);
},
click: function(event){
console.log('click', event);
}
});
listItem.appendTo(playlistItemListUl);
}
The listItem's click and contextmenu events successfully write to the console.log in this example. However, it is painfully slow and I wanted to reduce the lag induced from the bulk-add. So, I rewrote the code to incur just one DOM modification:
var playlistItemListUl = $('#PlaylistItemList ul');
var listItemsOuterHtml = [];
for(var i = 0; i < 1000; i++ ){
var listItem = $('<li/>', {
'data-itemid': item.get('id'),
contextmenu: function (event) {
console.log('contextmenu', event);
},
click: function(event){
console.log('click', event);
}
});
listItemsOuterHtml.push(listItem.prop('outerHTML'));
}
playlistItemListUl.append(listItemsOuterHtml.join(''));
This reduces the number of DOM manipulations required, but all of my nicely bound jQuery events are discarded.
Is there an effective middle-ground between these two styles which would allow me to create nodes using jQuery, instead of hand-crafting the HTML, but avoid unnecessary DOM insertions?
Why not add those click events to playlistItemListUl and let event delegation take care of the event? Like this :
playlistItemListUl.append(listItemsOuterHtml.join('')).on("click", "li", function(event) {
console.log('click', event);
});
Here the click event would be bound to the <ul/> and will get cascaded to <li/> when they are clicked on. And on is the replacement for live, bind and delegate. And, if you're binding multiple events,
playlistItemListUl.append(listItemsOuterHtml.join('')).on({
"click": function(event) {
console.log('click', event);
},
"contextmenu" : function (event) {
console.log('contextmenu', event);
}
}, "li");
And if you want it to work in this current setup, you must send out elements in listItemsOuterHtml, not its outerHTML alone.
You're pushing the outerHTML to an array, you should be storing DOM elements, not strings of HTML.
var playlistItemListUl = $('#PlaylistItemList ul'),
listItems = $([]);
for(var i = 0; i < 1000; i++ ){
var listItem = $('<li/>', {
'data-itemid': item.get('id'),
contextmenu : function (event) {
console.log('contextmenu', event);
},
click: function(event){
console.log('click', event);
}
});
listItems = listItems.add(listItem);
}
playlistItemListUl.append(listItems);

Implementing jQuery's "live" binder with native Javascript

I am trying to figure out how to bind an event to dynamically created elements. I need the event to persist on the element even after it is destroyed and regenerated.
Obviously with jQuery's live function its easy, but what would they look like implemented with native Javascript?
Here's a simple example:
function live(eventType, elementId, cb) {
document.addEventListener(eventType, function (event) {
if (event.target.id === elementId) {
cb.call(event.target, event);
}
});
}
live("click", "test", function (event) {
alert(this.id);
});
The basic idea is that you want to attach an event handler to the document and let the event bubble up the DOM. Then, check the event.target property to see if it matches the desired criteria (in this case, just that the id of the element).
Edit:
#shabunc discovered a pretty big problem with my solution-- events on child elements won't be detected correctly. One way to fix this is to look at ancestor elements to see if any have the specified id:
function live (eventType, elementId, cb) {
document.addEventListener(eventType, function (event) {
var el = event.target
, found;
while (el && !(found = el.id === elementId)) {
el = el.parentElement;
}
if (found) {
cb.call(el, event);
}
});
}
In addition to Andrew's post and Binyamin's comment, maybe this is an option:
With this you can use 'nav .item a' as the selector.
Based on Andrew's code.
function live (eventType, elementQuerySelector, cb) {
document.addEventListener(eventType, function (event) {
var qs = document.querySelectorAll(elementQuerySelector);
if (qs) {
var el = event.target, index = -1;
while (el && ((index = Array.prototype.indexOf.call(qs, el)) === -1)) {
el = el.parentElement;
}
if (index > -1) {
cb.call(el, event);
}
}
});
}
live('click', 'nav .aap a', function(event) { console.log(event); alert('clicked'); });
The other solutions are a little overcomplicated...
document.addEventListener('click', e => {
if (e.target.closest('.element')) {
// .element has been clicked
}
}
There is a polyfill in case you need to support Internet Explorer or old browsers.
An alternative to binding an event to dynamically to a specific element could be a global event listener. So, each time you update the DOM with another new element event on that element will also the "catches". An example:
var mybuttonlist = document.getElementById('mybuttonlist');
mybuttonlist.addEventListener('click', e=>{
if(e.target.nodeName == 'BUTTON'){
switch(e.target.name){
case 'createnewbutton':
mybuttonlist.innerHTML += '<li><button name="createnewbutton">Create new button</button></li>';
break;
}
}
}, false);
ul {
list-style: none;
padding: 0;
margin: 0;
}
<ul id="mybuttonlist">
<li><button name="createnewbutton">Create new button</button></li>
</ul>
In this example I have an event listener on the <ul> for click events. So, an event happens for all child elements. From the simple event handler I created, you can see that it is easy to add more logic, more buttons (with different or repeating names), anchors etc.
Going all in, you could add the eventlistener to document instead of the list element, catching all click events on the page and then handle the click events in the event handler.

how to attach onclick event to an array of elements (mootools)

Is there a syntax for the following thing:
$$('.a-lot-of-elems').addEvent('someevent',somefunction);
First off - the following will work just fine.
$$(selector).addEvents({
click: fn
});
Don't use for, to iterate through an element collection, use each instead:
$$(selector).each(function(el){
el.addEvents({
click: fn
});
});
Here's a working example: http://jsfiddle.net/EPYBx/
You are just missing the event type.
var someFunction = function(e) {
alert('clicked!');
}
$$('.a-lot-of-elems').addEvent('click', somefunction);
Alternatively, you can use
$$('.a-lot-of-elems').addEvent('click', function(e) {
alert('clicked!');
});
something like
var elements = $$('.a-lot-of-elems')
for(var i = 0 ; i < elements.length ; i = i + 1)
{
elements[i].addEvent(somefunction);
}
should do ya!

Categories