Cloned draggable with deepWithDataAndEvents = true unable to be dragged again - javascript

I want to be able to:
Clone draggables and be able to re-drag them
Keep data & events associated to the initial draggable div
Currently, with the .clone method set to clone(), the cloned draggable can be dragged but understandably the clone does not retain data from the initial draggable.
If clone(true) is used, the cloned draggable retains data from the initial draggable but cannot be dragged.
var dropped = jQuery(ui.draggable).clone().addClass("dropped").draggable(); //No data
var dropped = jQuery(ui.draggable).clone(true).addClass("dropped").draggable(); //Not draggable
Would anyone have any ideas what the problem might be? Any help is greatly appreciated. Here is the jsFiddle I have been working on - http://jsfiddle.net/wc71z5to/

If you are able to update to a slightly later version of jQuery (I use 19.2. in the JSFiddle), you can use delegated events for the click and not worry about using a deep clone.
http://jsfiddle.net/TrueBlueAussie/wc71z5to/2/
jQuery(document).on('click', '.component', function() {
alert($( this ).data( 'name' ));
});
It is not 100% obvious from the example what it is you need to retain, behaviour-wise, from the clones. If you can clarify I will try to ensure this meets your needs :)
A delegated event works by listening for events bubbling up to a non-changing ancestor. document is the default if there is nothing convenient/closer. It then applies the jQuery selector to the elements in the bubble chain. It then calls the function for any matching element that caused the event. This means the element does not have to match until event time, rather than when the event handler is created.
Note: Do not use 'body' for delegated events, as styling can cause the body to have 0 height and events may not occur! Always use document as the fallback.

Related

Cloned html markup lost events

I have invisible TR row which is cloned by jQuery by function clone(true, true) as $cloned but onclick events on some elements inside TR are lost.
Then I append into correct TABLE by:
jQuery(parent).closest('table').find('tbody').append($cloned);
What to check next or todo to clone events?
Old: That's by design, cloning does not copy event handlers.
New: Apparently, jQuery cloning does copy event handlers. While two options below are still valid approach, there might be some closure as event handler which holds "wrong" (from new nodes' point of view) references, and thus event handlers appear to be non-working.
Two options:
Clone and then re-assign handlers. If your code is well-written, that usually not a problem.
Bind your event handlers to outer container, possible to body or document, e.g.:
$(document).on('click', '.my-special-button', (e) => { ... });
Second approach might also require some rewriting. Still, it's a valid option. To find out more, look for "delegated event handlers" e.g. here: https://api.jquery.com/on/.
Both rules can be followed at the same time, that would be even better.

How to use jQuery filter function with event delegation?

I need to bind the change of all the textboxes(having class .box) under Box2 column who have the blue button(having class .blue which is dynamic in some cases) in their row.
Below is the screenshot of the HTML I have which is just a HTML table which has 3 columns(Box1,Box2 & for the button):
Class .blue could be present on some buttons on the page load & also could be removed/added to the buttons as result of another Ajax operation.
jQuery .on() has
.on( events [, selector ] [, data ], handler )
is it possible to use .filter() in the selector part where I can use a function like
jQuery('.box').filter(function(){
return jQuery(this).closest('tr').find('.blue').length
});
to filter out the needed textboxes & then bind there change event?
Considering the #parent is the parent div of the HTML I was trying
jQuery('#parent').on('change','.box',..); but this will listen to the change of all the box2 elements whereas I want it to listen to the specific ones only.
Ideas please?
Thanks.
In event delegation, we are binding events on the parent element and listening events on it based on the target(selector). The only thing we are certain about is the specific-selector of the target-element. Well, in the cases link specific-child or specific-sibling of the dynamically appended element, doing things in the handler(callback) is the only way!
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers.
Note: Attaching many delegated event handlers near the top of the document tree can degrade performance. Each time the event occurs, jQuery must compare all selectors of all attached events of that type to every element in the path from the event target up to the top of the document. For best performance, attach delegated events at a document location as close as possible to the target elements. Avoid excessive use of document or document.body for delegated events on large documents. [Reference]
use a if to test if the button(.box) has a blue class
$('.parent').on('change','.box2 input',function(){
if($(this).closest('tr').find('.box').is('.blue')) {
//do code here
}
});
Include a separate check for the button. Without any code it's not possible to give you the correct code
jQuery('#parent').on('change','.box',..);
if ($(this).next().hasClass('blue')) {
// Do other stuff
}

jQuery adding event handlers to eval'ed elements

I've a table generated dynamically by jQuery, using
this.html("<table><tr><td><div>Click Me</div></td></tr></table>");
within the table, I've a few divs (my sample shows only one to keep things simple), which I want to add click event handler to. I'd like to keep html clean and use as much of jQuery power as I can, but since I'm doing an 'eval' type of things I can't quite figure out how to do that.
I know, that I can use $("div[some attribute selector]").on("click", {}, clickHandler);, but is it a good idea in my case?
You need delegated events. To do that, simply use jQuerys on() method like this:
$(document.body).on('click', 'div', function( event ) {
// do something
});
Ref.: .delegate(), .on()
What is that? Almost all events do what we call 'bubble'. That means, if you click on a nested element, your browser looks if there is any click-event handler ascociated on that node. If so, it executes them and then the parent of that element is also asked if there is any click-event handler. This continues until either some handler prevents the event from further bubbling or we have reached the document.documentElement (html).
So, you should change the above document.body into the closest node relative to your dynamically added elements.
You can use either use live or delegate to do that

In jQuery, what's the proper way to "move" an element from its parent to another element?

Using jQuery 1.4 and jQueryUI 1.8
Specifically, I'm using draggables/droppables, and when dropped, I would like to move the draggable (it's children, events, etc) from belonging to its parent element to be appended/added as a child of the drop target.
I know that in the droppable drop option, I can supply the following callback:
function(event, ui) {
// stuff
}
where $(this).target will be the drop target, and ui.draggable will be the child element I would like to move - but I'm not sure the proper way to actually perform the move, preserving events, etc.
append() will remove the element and place it where you want.
$(this).target.append(ui.draggable);
// or, if $(this).target is not a jQuery object
var target = $(this).target;
$(target).append(ui.draggable);
Just use .append(), .appendTo(), .prepend() or .prependTo(). The detaching part is implicit. (I tested this by re-parenting entries in the jQuery Manipulation docs to each other.)
ui.draggable.appendTo($(this).target);

How do I register a Javascript event handler to an element that hasn't been added to the page yet

I'm trying to build a greasemonkey script which will dynamically create tables of data based on user interaction with... other dynamically created tables of data. My problem is that I'm having to make two passes every time I create a table: one to create the table, and another to go grab all of the objects in the table I want to add event handlers to (by id) and add the various event handlers to them.
If I attempt to, say, add an onClick event to a table td before I've created the table and inserted it into the HTML, I get a "component is not available" exception.
This is incredibly cumbersome, because I either have to maintain, separately, a list of the ids and what I should do to those elements when I make my second pass to add the handlers, or develop a naming convention by which I know, based on the id, what I should do with the element.
There HAS to be a better way to do this. I just haven't figured it out yet. Anyone have any ideas?
Firstly, I'd love to know why you need a different ID for every single TD. Is the ID holding important information, such as an index? In this situation it might be better creating each TD within a loop. Also, obviously you can't attach an event handler to a DOM element which doesn't exist! It doesn't have to be injected into the DOM but it DOES have to exist in some capacity.
jQuery's live() isn't a magical mystery, it just uses event delegation, so it attaches the event to a parent element, such as the table and then decides what happens dependent on the target of the click. Here's a rudimentary example. I register a handler to the 'body' element, and then I test each time to see what the target is, if it's a TD element I doSomething() ->
document.body.onclick = function(e) {
var realTarget = e ? e.target : window.event.srcElement;
if ( realTarget.nodeName.toLowerCase() === 'td' ) {
doSomething();
}
};
Event delegation relies on something called event bubbling (or "propogation") which is the way in which modern browsers implement the event model. Each event, when triggered will travel upwards through the DOM until it can go no further. So if you click on an anchor within a paragraph the anchor's 'click' event will fire and THEN the paragraph's 'click' event will fire etc. etc.
jQuery 1.3+ has a new live() function that can set up event handlers for elements that don't exist yet .. check it out
You have to wait for the element to be added to the page, then add the event handler then.
There is no easy way to say "add this to all elements of this type, now and in the future".
It is possible to have a timer periodically check the page for new elements, applying a queue of events (or other properties) to them as they appear, all behind the scenes. This can be abstracted out and re-used, for example Jquery can do that sort of thing.
As JimmyP pointed out, your problem can easily be solved using event bubbling. You might consider writing a wrapper function to work around browser inconsistencies - my own version can be found here and would be used like this:
capture('click', '#element-id', function(event) {
// `this` will be the originating element
// return `false` to prevent default action
});

Categories