How to clone jQuery element with its data - javascript

How can we clone jQuery elements with them data?
Using .data("dummy", "test") I set "test" data to "dummy" key of selected elements. Using .data("dummy") it returns "test". After cloning .data("dummy") returns undefined.
How can I avoid this?
$(".save").on("click", function () {
var dummy = $(this).data("dummy");
if (dummy) {
return alert(dummy);
}
$(this).data("dummy", "I am a button");
var $clone = $(this).clone();
$(this).after($clone);
});
JSFIDDLE

You were just missing 1 parameter...
http://jsfiddle.net/DEKFn/2/
Notice the use of true in the clone(). That determines whether to copy data and events when cloning the element, as per the docs..
http://api.jquery.com/clone/
$(".save").on("click", function () {
var dummy = $(this).data("dummy");
if (dummy) {
return alert(dummy);
}
$(this).data("dummy", "I am a button");
var $clone = $(this).clone(true);
$(this).after($clone);
});
You've also asked how to copy just the data - not the events. This isn't possible, but you can remove all the event handlers like this...
var $clone = $(this).clone(true);
var $clone.off();

clone takes an argument withDataAndEvents so do:
var $clone = $(this).clone(true);
to get only data just turn off the events.
var $clone = $(this).clone(true).off();
Fiddle

The data is attached to the element as a simple javascript object. Simply duplicate the data and assign it to the clone.
$(".save").on("click", function () {
var dummy = $(this).data("dummy");
if (dummy) {
return alert(dummy);
}
var $this = $(this);
$this.data("dummy", "I am a button");
var $clone = $this.clone();
var $data = $.extend(true,{},$this.data());
$clone.data($data);
$(this).after($clone);
});
More on .extend() http://api.jquery.com/jQuery.extend/
Fiddle here
http://jsfiddle.net/9Q7EM/
EDIT:
Not sure why this was downvoted. You incur some overhead copying both the data and the events as the original answer suggested. Both are valid.

Related

finding value in nested jquery array

I cant get that to work:
My json
[{"myicons":[{"icon":[{"rel":"1","id":"icon1","class":"bookmark desktop-icon ui-draggable","title":"bookmark1"}]},{"icon":[{"rel":"2","id":"icon2","class":"bookmark desktop-icon ui-draggable","title":"bookmark2"}]}]}]
My jquery each function finds the 2 icons but i cant seem to get the values... it keeps saying undefined.
var myicons = data[0].myicons;
$.each(myicons, function() {
var iconid = this.id;
alert(iconid);
});
Your JSON is full of array. i,e. data, myicons and even icon
$.each(data, function () {
var myicons = this.myicons;
$.each(myicons, function () {
var iconid = this.icon[0].id;
alert(iconid);
});
});
DEMO
I strongly suggest you to simplify yous JSON object

Cannot call method 'find' of undefined

I'm trying to use the objects within my jQuery code.
I've nearly this:
var opts = {
ul: $(this).find('.carousel'),
li: ul.find('li')
}
li property gives an error Cannot call method 'find' of undefined
How can it be fixed?
It doesn't matter what your selector is, you can't access a property of an object that you are declaring, while you are declaring it.
Why would ul be declared? You're making a property ul on your opts object.
Ugly:
What you might want is:
var opts = {
ul: $(this).find('.carousel')
}
opts.li = opts.ul.find('li');
But if you don't actually need references to both groups then:
Cleaner:
var opts = {
li: $(this).find('.carousel li')
}
is just as good.
Cleanest:
You could also do:
var $carousel = $(this).find('.carousel');
var options = {
carousel: $carousel,
carouselItems: $carousel.find('li')
}
Godawful, but you asked for it:
var CarouselOptions = (function () {
var options = function (carousel) {
this.carousel = $(carousel);
this.carouselItems = this.carousel.find('li');
};
options.prototype.myOptionsFunction = function () {
// Don't know what you want your object to do, but you wanted a prototype...
};
return options;
})();
var opts = new CarouselOptions($(this).find('.carousel'));
Also
(Be careful with what your this is, presumably you have more than one .carousel element on the page, and here you want the one that is within the target of an event.)
Your error message is essentially saying that $(this) is undefined (or in other words, jQuery couldn't find this element). Because you don't have any code other than the single object you are trying to set, I don't know what the actual value of this is.
What I would do is ensure that this is set to an element of some sort. A simple console.log(this) should handle that. If this isn't an HTML element, then that's your problem. Either ensure you are inside of a jQuery event function like this:
$('#id').click(function() {
this === document.getElementById('id'); // true
});`
Or you can just drop the $(this):
var opts = {};
opts.ul = $('.carousel'),
opts.li = opts.ul.find('li')
var that = $(this);
var opts = {
ul: that.find('.carousel'),
li: ul.find('li')
}

Classes in JavaScript using prototype

I have a problem, I want to create a JavaScript class:
function Calculatore(txt,elements) {
this.p= new Processor();
this.output=txt;
$(elements).click(this.clickHandler);
}
Calculatore.prototype.clickHandler = function() {
var element=$(this);
// Code Here
// "this" contains the element.
// But what if I want to get the "output" var?
// I tried with Calculatore.prototype.output but no luck.
}
So how can I solve this?
You can use jQuery's $.proxy:
function Calculatore(txt,elements) {
this.p= new Processor();
this.output=txt;
$(elements).click($.proxy(this.clickHandler, this));
}
Calculatore.prototype.clickHandler = function(event) {
var clickedElement = event.target;
alert(this.output);
}
Edited. Jason brought up a good point in the comments. It's probably better to use event.target which references only the element clicked, rather than elements which may reference an array of objects matching the selection.
You have a collision between this values. You currently don't have access to the instance because this has been set to the element inside a click handler.
You could make a proxy function to pass both the this value (the element) and the instance:
function Calculatore(txt,elements) {
this.p= new Processor();
this.output=txt;
var inst = this; // copy instance, available as 'this' here
$(elements).click(function(e) {
return inst.clickHandler.call(this, e, inst); // call clickHandler with
// 'this' value and 'e'
// passed, and send 'inst'
// (the instance) as well.
// Also return the return
// value
});
}
Calculatore.prototype.clickHandler = function(e, inst) {
var element = $(this);
var output = inst.output;
};

Anyway to get an indexed item from $(".box")

Suppose I get a list of items like $(".box"). Is it possible to get an indexed jQuery object
like
var $boxes = $(".box"),
$box2 = $boxes[1]
currently I do something like
var $boxes = $(".box");
$boxes.each(function(i, box) {
var $box = $(box); // <-- is this a good idea?
// do something with $box
});
I wonder tho if the line var $box = $(box) is such a good idea? I am actually running that in a setInterval()
like
var $boxes = $(".box");
setInterval(function() {
$boxes.each(function(i, box) {
var $box = $(box); // <-- is this a good idea?
// do something with $box
});
}, 1000);
I wonder if its bad for performance since I am initializing a variable for each item in $boxes per 1s in this example. If I can access the element directly from the jQuery "array" or whatever $boxes is, it maybe better?
It's not entirely clear what your question is, but jQuery objects are already array-like, you can use the [] operators on them. What you get back is the raw DOM object at that index, so:
var $boxes = $(".box"),
box2 = $boxes[1], // `box2` is a raw DOM object
$box2 = $(box2); // `$box2` is a jQuery wrapper around the second box
Regarding this code:
var $boxes = $(".box");
setInterval(function() {
$boxes.each(function(i, box) {
var $box = $(box); // <-- is this a good idea?
// do something with $box
});
}, 1000);
It's fine to do that provided you really need to do it (e.g., if you really need a jQuery wrapper around that specific entry). It is making the browser do work each time the interval timer fires (because $() isn't free, though it's not expensive either), so if the list is short, you could trade that CPU time for memory use by pre-creating the jQuery wrappers on the elements:
var $wrapped = [];
$(".box").each(function() {
$wrapped.push($(this));
});
setInterval(function() {
$.each($wrapped, function(i, $box) {
// do something with $box
});
}, 1000);
You can iterate though the jQuery elements as per your example, nothing wrong with that. Creating a local variable for each element var $box = $(box); is a good idea.
You can also access the elements of the jQuery object with the eq method, e.g:
var $boxes = $(".box"),
$box2 = $boxes.eq(1);
That way you don't need to pass the element through the $ constructor.
If you want a faster and more efficient way of looping through the elements while also having a jQuery wrapper for each one, check out Ben Alman's "each2" plugin:
http://benalman.com/projects/jquery-misc-plugins/#each2
Then you could replace this code:
$boxes.each(function(i, box) {
var $box = $(box);
// do something with $box
});
With this:
$boxes.each2(function(i, $box) {
// do something with $box
});
On a side-note,
var $boxes = $(".box");
setInterval(function() {
$boxes.each(function(i, box) {
var $box = $(box);
// do something with $box
});
}, 1000);
is equivalent to
var $boxes = $(".box");
setInterval(function() {
$boxes.each(function() {
var $box = $(this);
// do something with $box
});
}, 1000);
'i' in the callback function of each is the index. But I wouldn't recommend it. Simply call 'this' inside the each function for the current element.
var $box = this;
If you only need i.e. box 3:
$('.box:eq(3)').doStuff();

jQuery: Finding cloned child in cloned parent?

Let's say I have this jQuery extension method:
$.fn.foobar = function() {
var clone = this.parent().clone();
};
After I've gotten clone, how can I find the cloned child element that is the same as this?
Will this work?
$.fn.foobar = function() {
var clone = this.parent().clone();
var cloneOfThis = clone.find(this);
};
Or this?
$.fn.foobar = function() {
var clone = this.parent().clone();
var self = this;
var cloneOfThis;
clone.children().each(function() {
var $this = $(this);
if ($this === self) {
cloneOfThis = $this;
}
});
};
You could try giving it some unique class that could be used to refer back to the proper element.
$.fn.foobar = function() {
// Add a class to "this", then clone its parent
var clonedParent = this.addClass("someUniqueClass").parent().clone();
// Reference the new clone of "this" inside the cloned parent,
// then remove the class
var cloneOfThis = clonedParent.find(".someUniqueClass").removeClass("someUniqueClass");
// Remove the class from the original
this.removeClass("someUniqueClass");
};
You can't get a reference comparison to work here because this isn't in the clone, it's the original element, it wasn't moved. An element like the one you cloned is in the cloned parent, so you have to decide what "the same" means, is it the same ID, the same HTML content, the same value?
You just need to pick a value you can compare, because the reference won't work here...you can't find something that isn't there :)
Taking patrick dw's answer a little further, and incorporating Felix King's comment, I would suggest the following:
$.fn.foobar = function() {
return $(this).each(function() {
// Add a class to "this", then clone its parent
var clonedParent = $(this).addClass("someUniqueClass").parent().clone();
// Reference the new clone of "this" inside the cloned parent
var cloneOfThis = clonedParent.find(".someUniqueClass");
//remove the unique class to preserve the original state (other than the clone which is now present)
$('.someUniqueClass').add(cloneOfThis).removeClass('someUniqueClass');
});
};

Categories