Why is clone only cloning item once? - javascript

I'm new to jquery and I'm 100% sure I'm making a logical error but I can't see it. Basically as a user types a item in a field I want to clone the fields so they can continue adding more info. In the example I'm working on, I'm trying to create a list of siblings and their age.
Here's my code:
$(document).ready(function () {
$("input[name='age']").on('keydown', function() {
//is it the last input?
if (this == $("input[name='age']:last", this.form)[0]) {
//insert an empty clone of the current input
//$(this).after($(this).clone(true).val(''));
$('.family').clone().insertAfter(".family");
}
Here's the html:
<form>
<div class="family">
<input name="age" value=0> <input name="sibling" value="name?">
<hr />
</div>
</form>
If I use $(this).after($(this).clone(true).val('')); then it works but it only clones one field(the age one) so I tried to replace it with $('.family').clone().insertAfter(".family"); to clone the div class but it clones the fields only once. If I start typing my first siblings info then the second form will appear but if I start typing on the second form then nothing new appears after that.
Its just a guess but I think the if statement is not matching so the clone isn't being created(I'm new so this idea could be wrong). If this is the case then I'm confused because I'm cloning the same names of the input fields so input[name='age']:last should match the last age field..not sure.
Any idea what I'm doing wrong?

You're going to have to make your keydown event handler take note of future elements as well (i.e. the new <div> elements you're cloning and inserting).
Change
$("input[name='age']").on('keydown', function() {
to
$('form').on('keydown', 'input[name="age"]', function () {
i.e. (with some corrections / optimizations as well)
$('form').on('keydown', 'input[name="age"]:last-child', function() {
var _p = $(this).parent('.family');
// insert an empty clone of the current input's containing div
// ADDED : ... after the current input's containing div
_p.clone().insertAfter(_p);
}

Try this:
$("form").on('keydown',"input[name='age']:last", function() {
$('.family:last').clone().insertAfter(".family:last");
})
jsFiddle example
The issue is that you're trying to bind to elements that don't yet exist. To do that using .on(), you just need to bind to an element that exists in the dom:
Event handlers are bound only to the currently selected elements; they
must exist on the page at the time your code makes the call to .on().
To ensure the elements are present and can be selected, perform event
binding inside a document ready handler for elements that are in the
HTML markup on the page. If new HTML is being injected into the page,
select the elements and attach event handlers after the new HTML is
placed into the page. Or, use delegated events to attach an event
handler, as described next.
Then, you want to make sure you're only checking and cloning the last div and it's input. Note that when you're cloning the input fields, you will need to either change the names so you don't end up with a bunch of them that use the same name, or with a language like PHP you can append [] after the name and PHP will parse that into an array.

Related

Using .jquery .on() without an event

I have input elements that get appended to my HTML document that I need to get the value of. I understand that .on() should be used to get appended elements, but .on() expects an event to activate it, and I don't need an event.
Below is the code. #save_design is on a different part of the page. #fields_cnt is the parent that holds all my appended forms. .reg_field is the element I'm trying to get the value of. The first child works because that form is present when the document is loaded, or in other words, it is not appended. The other children all returns values undefined, or false depending on the input type and the logic I'm applying to it. This leads me to believe that .on() or something close to it should be used.
To clarify, I am trying to get the value of an appended input when #save_design is clicked.
$('#save_design').click( function() {
group_text[form_count] = $('#fields_cnt .field_group:nth-child(' + child + ') .reg_field').val();
I have tried replacing the originating click event as so. It has not worked.
$('body').on('click', '#save_design', function(){
I have tried using .on() without an event or event object. It has also not worked.
$('body').on('', '', function(){
group_text[form_count] = $('#fields_cnt .field_group:nth-child(' + child + ') .reg_field').val();
});
I have found a plugin that should work.
JQuery selecting dynamically generated html without adding event (with live)
This is not ideal, because as stated in the github readme, it is being rewritten. Also, the SO answer is 3 years old now, so it may no longer be the correct answer.
https://github.com/brandonaaron/livequery
I'd like to find a more direct solution. Is there a way to do this natively in jQuery? Is there a way to use .on() without an event, and if so, what's the appropriate syntax?
UPDATE: Clarification: The input fields get appended when an add field button is pressed. Their values change after they are appended. I would prefer not to rerecord the value of an input, every time it is altered.
UPDATE Here is the HTML structure to give a better idea of how I'm defining child.
<div id="fields_cnt">
<div id="field_group_1" class="field_group form-group">
<input placeholder="Field" class="reg_field form-control" />
</div>
<!-- This is appended by jQuery. There may be multiple of these -->
<div id="field_group_2" class="field_group form-group">
<input placeholder="Field" class="reg_field form-control" />
</div>
</div>
I have found a great solution to my problem. It takes a different approach on selecting the element, but ultimately works and results in drier code.
Here, I use each, to grab each instance of the appended element I am trying to select. This returns the desired value.
$('.reg_field').each( function() {
group_text[form_count] = $(this).val();
form_count++;
});
I suspect the issue was my previous selector was unable to use $(this), which seems to work in more cases for appended elements. Another possibility that was brought up in the comments was the use of nth-child in the middle of a selection. I am not sure on these points, and would appreciate a better explanation. Hopefully this will help for someone who faces the same issue. Also, I appreciate the help on getting me to focus on the selection rather than the binding.

jQuery: last doesn't work with mixed tags

I am having html look like below:
<td id="sample">
<select>
<option>
</select>
<br/>
<select>
<option>
</select>
<br/>
//...
</td>
I know we should be using div instead of table but our whole webpage is designed this way and it is kind of time-consuming to change it.
What I am trying to do is add a event handler to the change of last select tag. I have tried:
$("#sample > select:last") and $("#sample").find("select).last() but both of them tend to find the last select before br tag which means the first one. I can probably solve this issue with adding an attribute in my code but that will result in adding a global variable and an amount of code. Is there a cleaner solution?
Edit:
Sorry that I didn't understand my issue correctly and many thanks to all the comments. I guess my issue actually is I put the event handler in my document ready section and append more <select> in that event handler. So every time when I am trying to trigger the event, it always come to the original last element.
My code looks like:
$('#sample > select:last').on('change', function() {
$('#sample > select:first').clone().show().appendTo('#sample');
$("<br/>").appendTo('#sample');
});
But although I kind of find the reason, I am still confused in solving it. Is there any solutions around this?
You're dynamically creating your elements, therefore you need a dynamic event delegation:
Dynamic Delegation works like:
$("staticElement").on("someevent", "dynamicElement", function)
and it's used to attach an event to the parent with a renewal look for recently appended child Elements.
To explain, on execution time if you attach an event to some elements directly like: $(".foo").click(fn) or $(".foo").on("click", fn) it will not account for .foo elements added in a later time cause they were not in the .foo collection at the time.
By assigning the event to the static parent, the event Delegation will bubble inversely searching for children (that match the selector argument and are the initiators of the same event trough event.target) that received that event.
In your example:
// static Parent // evt , dynamic
$("#sample").on("change", "> select:last", function() {
$('#sample > select:first').clone().show().appendTo('#sample');
$("<br/>").appendTo('#sample');
});
More details to your question here: http://api.jquery.com/on/#direct-and-delegated-events

.on() doesn't work on appended element(jQuery)

So I'm appending a row in a table and then upon clicking a button I want to remove that row.
However I can't do it in a way that seems natural, like:
$(".usun").on("click", function(){
$(this).parent().parent().remove();
});
where .usun is a class of buttons that remove a row in a table. Of course this fragment of code is inside of $(document).ready(.... What seems to work for me is this:
$(this).on("click", ".usun", function(){
$(this).parent().parent().remove();
});
and I don't understand why. Could someone explain me this?
There are several method signatures available for .on() which you can read about here.
Your first syntax ($(".usun").on() is, just one time, attaching click handlers to each individual .usun element. If more are added later, they don't have click handlers.
The second syntax ($(this).on("click", ".usun", function()...) attaches a single listener to the document, saying "any time a .usun inside me is clicked, do something". This covers a case where more matching elements are added after $(document).ready is fired.
That is because that specific .usun does not exist yet when you are registering the event.
You need to register the event to its parent element (prefereably the table id) like so:
$('#yourtableid').on("click", ".usun", function(){ //if table id does not exist, you can use $('body') instead
$(this).parent().parent().remove();
});
This will make sure that you are attaching the events even to 'unborn' elements.
Hope this helps!
So you're aware of this... your question is related to live and non-live lists (collections) in javascript;
review the following Javascript,
var queryStatic = document.querySelectorAll(".usun"); //non-live | static list
var queryLive = document.getElementsByTagName("div"); //live list
queryStatic will return a NodeList that statically references the nodes that satistifed the condition of the query.
If you were to alter/modify the properties/fields/members of a node or it's element that is referenced by this NodeList outside of using the queryStatic, it would be represented in the queryStatic instance;
you cannot, however, add a new element/node to the DOM that would have satisfied the query and expect it to be in that NodeList.
queryList (an HTML Collection), however, will represent both aspects. It is a live list.
The point is, the underline concept has little to nothing to do with method signatures.
Edit
However, in the case of jQuery's .on() method; there is support for attaching the listener to a parent element (like Document) and specifying an additional query criteria.

jQuery selector stores object? How to force it not to?

I have a dropdown ( < select > ) element and a container.
<div class='container' />
<script>
var dropdown = "<select class='multi-dropdown'> ... </select>"
</script>
When the value is changed, You get another of it.
It is logical, that this only happens when the document is ready ( the first one is made there ), and when the client modifies the last one.
$(document).ready( function(){
$('.container').append(dropdown);
$('.multi-dropdown:last').change(function(){
$('.container').append(dropdown);
});
});
Seems to be a working code for me. But what I noticed is, this is not working with the next appended dropdown element. Also if I change the original one, it fires.
My theory is, maybe jQuery already stored the original object as the :last , so it won't select a new element again even if I add new "last" ones.
or
The freshly created element ( this way ) isn't even selectable with jQuery.
Please argue in favor, or against, these are just my ideas.
The issue is that you're newly appended SELECT doesn't ever get an event bound to it. Just because you used the class to bind the change even for the initial SELECT, that doesn't automatically apply to every newly added element to the DOM.
Read up on delegated events, like for the on() function. Or use something like livequery.
Because you are binding the change handler to the :last element at page startup, if the last element changes dynamically it will not be changed accordingly; try and read deeply delegation using on method instead.
Like:
$(document).ready( function(){
$('.container').append(dropdown);
$('body').on('change','.multi-dropdown:last',function(){
$('.container').append(dropdown);
});
});

Recursive jQuery function to amend select option values

I have a form that I am trying to alter with jQuery. Basically, my form has two elements and I need to change the value of the first option in each of them. However, there is an "add more" option that uses AJAX to dynamically generate another element that also needs changed. This add more button can be clicked an unlimited amount of times.
Right now I have this:
$(document).ready(function(){
$("#myname-0-field option:first").val("None");
$("#myname-1-field option:first").val("None");
});
This works fine, but once the "add more" button is clicked, I have more elements called "#myname-2-field", "#myname-3-field", "#myname-4-field" etc. These obviously aren't affected by adding another line into my jQuery as the document has already loaded when they are added.
So the real question is, can someone point me in the right direction of writing a function that can react when the new element is added and change it. If possible, I'm also looking for the function to be aware and look for "#myname-X-field option:first" for tidyness.
use live() function
Then using each function set value
From the jQuery API look live function
Maybe you could add class to your element, so that finding particular element would be easier and it would not add event to other similar elements.
In the example I have a Li with class
$('li.myClass').live('click', function() {
$(this).val(); // this is the getter for clicked value
$(this).val("some_value_here"); // this is the setter for clicked value
});
Now you can add more elements (that has myClass class) and it will have a click event.
Btw. if you know that all elements are inside some container (div for example) then you can write more efficient jQuery using delegate.
$('#container_id').delegate('li.myClass', 'click', function () {
});
This is more efficient because it looks your new elements only under "containter" not from the whole DOM structure.

Categories