jQuery - Clone <div> with form elements, including their values - javascript

I have a Django formset, which has lots of form elements. I have a working method to create a new empty form, but I also need a solution to copy a form. Technically, all forms are in a div, like this:
<form>
...
<div class="subform">
...
</div>
...
</form>
The problem is, that in latest jQuery versions, html and replaceWith methods don't handle input values, so they create empty form elements instead - which is not the behavior I need. Of course I could implement an algorithm which iterates over all the elements, copies them and sets their values, but I'd like to avoid it if possible.
I want to replace an existing form with the copied values.

Yes can use clone() and replaceAll() instead of html()/replaceWith(). clone() will preserve the value properties of your <input> elements, and can even preserve event handlers and custom data.
You can write something like:
var theForm = $("selector-matching-your-form");
theForm.clone(true).replaceAll(theForm);

Frédéric has a brilliant answer, and it took me a while to understand it. Here is a different version:
Make a variable that references an element.
var someElement = $("input#some-element");
Clone the element
var cloneOfElement = $someElement.clone(true);
Use the clone to replace original element, by using the variable that references the original element.
cloneOfElement.replaceAll( $someElement );
--
From the replaceAll() documentation:
"The .replaceAll() method is similar to .replaceWith(), but with the source and target reversed."

Related

jQuery: Chain selectors off of DOM element variables

I have this event handler from kendo's drag and drop UI and I've assigned it to a variable, lets say e.
I'm trying to select divs that have ID's containing a certain string. I know the syntax for that when continuing from classic jQuery selectors, i.e.
$('select div[id*="whatever"]')
But can I do this off of a variable that contains a DOM element? I know this is a rather simple question but google has thus far been unable to help me.
If you want to filter a preexisting jQuery result, you can go with this:
var $allDivs = $("div"),
$certainDivs = $allDivs.filter("[id*='whatever']");
I believe you might want .find
// returns all DOM elements that
// (1) are descendants of e
// (2) match the selector
e.find("selector");
If e isn't wrapped with jQuery, you might need to do $(e) instead.

Get Forms (Jquery vs Javascript)

What would the equivalent of
document.forms[0].submit();
...be in jquery format? I know it would look like something similar to:
$("form").submit()
But i'm not sure how to just send a general form without knowing it's id
Since your javascript code is trying to submit first form in the page, in jQuery you've multiple ways to achieve it, one way is to use .first():
Reduce the set of matched elements to the first in the set.
$("form").first().submit()
$("form").eq(0).submit()
Given a jQuery object that represents a set of DOM elements, the .eq()
method constructs a new jQuery object from one element within that
set. The supplied index identifies the position of this element in the
set.
The first submits the first form on the page.
The jQuery equivalent would be:
$("form:eq(0)").submit();
But if it works in plain JavaScript, you shouldn't change it to jQuery! If it isn't broken, don't "fix" it!

Jquery clone() doesn't clone new value set wit data()

Using jquery data() to set data attribute of an element, like so:
HTML:
<div id="some-el" data-number="0"></div>
JQ:
$("#some-el").data("number",1);
As we know, data changes variable internally. So inside inspector you cannot actually see that new value is 1. But this aside, if I do clone on the element with new data value, jquery clones original dom element without current data value!!!
$("#some-el").clone();
Results in <div id="some-el" data-number="0"></div> both internally and visibly!
I was thinking I could avoid this problem by simply using attr("data-number",1);
Anyways, I wanted to ask you if this is correct behaviour of dat()? Is what I'm seeing expected? and WHY?
I think clone can accept a boolean to indicate a Clone with data and events, so Clone(true) should work: http://api.jquery.com/clone/
Here's a fiddle that works: http://jsfiddle.net/2pdNL/
.data() is not setting the value in DOM.
The data- attributes are pulled in the first time the data property is
accessed and then are no longer accessed or mutated (all data values
are then stored internally in jQuery)
But here is a workaround, instead of using
$("#some-el").data("number",1);
Interact directly to DOM like
$("#some-el").attr("data-number",1);
JSFiddle
Also check this answer

How can I access a particular div on a page which has the same id in two places?

This is the same question as this:
Referring to a div inside a div with the same ID as another inside another
except for one thing.
The reason there are two elements with the same ID is because I'm adding rows to a table, and I'm doing that by making a hidden div with the contents of the row as a template. I make a new div, copy the innerhtml of the template to my new div, and then I just want to edit bits of it, but all the bits have the same ID as the template.
I could dynamically create the row element by element but it's a VERY complex row, and there's only a few things that need to be changed, so it's a lot easier to just copy from a template and change the few things I need to.
So how do I refer to the elements in my copy, rather than the template?
I don't want to mess up the template itself, or I'll never be able to get at the bits for a second use.
Or is there another simpler way to solve the problem?
It will probably just be easiest when manipulating the innerHtml to do a replace on the IDs for that row. Maybe something like...
var copiedRow = templateRow.innerHTML.replace(/id=/g,"$1copy")
This will make the copied divs be prefixed with "copy". You can develop this further for the case that you have multiple copies by keeping a counter and adding that count variable to the replace() call.
When you want to make a template and use it multiple times its best to make it of DOM, in a documentFragment for example.
That way it doesn't respond to document.getElementById() calls in the "live" DOM.
I made an example here: http://jsfiddle.net/PM5544/MXHRr/
id's should be unique on the page.
PM5544...
In reality, there's no use to change the ID to something unique, even though your document may not be valid.
Browsers' selector engines treat IDs pretty much the same as class names. Thus, you may use
document.querySelector('#myCopy #idToLookFor');
to get the copy.
IDs on a page are supposed to be unique, even when you clone them from a template.
If you dynamically create content on your page, then you must change the id of your newly cloned elements to something else. If you want to access all cloned elements, but not the template, you can add a class to them, so you can refer to all elements with that class:
var clonedElement = template.cloneNode(yes); // make a deep copy
clonedElement.setAttribute("id", "somethingElse"); // change the id
clonedElement.setAttribute("class",
clonedElement.getAttribute("class") + " cloned"
);
To access all cloned elements by classname, you can use the getElementsByClassName method (available in newer browsers) or look at this answer for a more in-depth solution: How to getElementByClass instead of GetElementById with Javascript?
Alternatively, if you have jQuery available, you can do this is far less lines of code:
$("#template").clone().attr("id","somethingElse")
.addClass("cloned").appendTo("#someDiv");
The class lookup is even simpler:
$(".cloned").doSomethingWithTheseElements();
Try to avoid using IDs in the child elements of the cloned structure, as all ids of the cloned element should be changed before adding the clone to the page. Instead, you can refer to the parent element using the new id and traverse the rest of the structure using classnames. Class names do not need to be unique, so you can just leave them as they are.
If you really must use ID's (or unique "name" attributes in form fields), I can strongly suggest using a framework like jQuery or Prototype to handle the DOM traversal; otherwise, it is quite a burden to resolve all the cross-browser issues. Here is an example of some changes deeper in the structure, using jQuery:
$("#template").clone().attr("id","somethingElse")
.addClass("cloned") // add a cloned class to the top element
.find("#foo").attr("id","bar").end() // find and modify a child element
.appendTo("#someDiv"); // finally, add the node to the page
Check out my ugly but functional cheese. I wrote a function that works like getelementbyid, but you give it a start node instead of the document. Works like a charm. It may be inefficient but I have great faith in the microprocessors running today's browsers' javascript engines.
function getelement(node, findid)
{
if (node)
if (node.id)
if (node.id == findid)
return node;
node = node.firstChild;
while(node)
{
var r = getelement(node, findid);
if (r != null)
return r;
node = node.nextSibling;
}
return null;
}
When you copy the row, don't you end up having a reference to it? At that point can't you change the ID?

Break up a form with jQuery?

I have a form, which I want to iterate through. I want to show one fieldset at a time, and then show a "next" and "back" button to go to the next section.
I'm assuming that I start with $('fieldset'); but how do I access individual elements thereafter?
$("fieldset")[i] does not seem to work.
How would I accomplish that with jQuery?
I don't necessarily recommend this, but:
$($('.fieldset')[i]).css(...)
Should work.
If you wrap each call to $('.fieldset')[i] in a new JQuery selector, you create a new JQuery object out of that single item. JQuery objects have the method css that you want. Regular dom objects do not. (That's what you get with $('.fieldset')[i])
From the jQuery documentation:
How do I pull a native DOM element from a jQuery object?
A jQuery object is an array-like
wrapper around one or more DOM
elements. To get a reference to the
actual DOM elements (instead of the
jQuery object), you have two options.
The first (and fastest) method is to
use array notation:
$('#foo')[0]; // equivalent to
document.getElementById('foo') The
second method is to use the get
function:
$('#foo').get(0); // identical to
above, only slower You can also call
get without any arguments to retrieve
a true array of DOM elements.
To get a jQuery wrapper back around the DOM element you just extracted, rewrap it like so:
$( $('#foo')[0] ) //now it's ajQuery element again.
$("fieldset").each(function() {
// code, applied for each fieldset
})

Categories