jQuery clone duplicate IDs - javascript

I have an HTML element with a large collection of unordered lists contained within it. I need to clone this element to place elsewhere on the page with different styles added (this is simple enough using jQuery).
$("#MainConfig").clone(false).appendTo($("#smallConfig"));
The problem, however, is that all the lists and their associated list items have IDs and clone duplicates them. Is there an easy way to replace all these duplicate IDs using jQuery before appending?

If you need a way to reference the list items after you've cloned them, you must use classes, not IDs. Change all id="..." to class="..."
If you are dealing with legacy code or something and can't change the IDs to classes, you must remove the id attributes before appending.
$("#MainConfig").clone(false).find("*").removeAttr("id").appendTo($("#smallConfig"));
Just be aware that you don't have a way to reference individual items anymore.

Since the OP asked for a way to replace all the duplicate id's before appending them, maybe something like this would work. Assuming you wanted to clone MainConfig_1 in an HTML block such as this:
<div id="smallConfig">
<div id="MainConfig_1">
<ul>
<li id="red_1">red</li>
<li id="blue_1">blue</li>
</ul>
</div>
</div>
The code could be something like the following, to find all child elements (and descendants) of the cloned block, and modify their id's using a counter:
var cur_num = 1; // Counter used previously.
//...
var cloned = $("#MainConfig_" + cur_num).clone(true, true).get(0);
++cur_num;
cloned.id = "MainConfig_" + cur_num; // Change the div itself.
$(cloned).find("*").each(function(index, element) { // And all inner elements.
if(element.id)
{
var matches = element.id.match(/(.+)_\d+/);
if(matches && matches.length >= 2) // Captures start at [1].
element.id = matches[1] + "_" + cur_num;
}
});
$(cloned).appendTo($("#smallConfig"));
To create new HTML like this:
<div id="smallConfig">
<div id="MainConfig_1">
<ul>
<li id="red_1">red</li>
<li id="blue_1">blue</li>
</ul>
</div>
<div id="MainConfig_2">
<ul>
<li id="red_2">red</li>
<li id="blue_2">blue</li>
</ul>
</div>
</div>

$("#MainConfig")
.clone(false)
.find("ul,li")
.removeAttr("id")
.appendTo($("#smallConfig"));
Try that on for size. :)
[Edit] Fixed for redsquare's comment.

I use something like this:
$("#details").clone().attr('id','details_clone').after("h1").show();

This is based on Russell's answer but a bit more aesthetic and functional for forms.
jQuery:
$(document).ready(function(){
var cur_num = 1; // Counter
$('#btnClone').click(function(){
var whatToClone = $("#MainConfig");
var whereToPutIt = $("#smallConfig");
var cloned = whatToClone.clone(true, true).get(0);
++cur_num;
cloned.id = whatToClone.attr('id') + "_" + cur_num; // Change the div itself.
$(cloned).find("*").each(function(index, element) { // And all inner elements.
if(element.id)
{
var matches = element.id.match(/(.+)_\d+/);
if(matches && matches.length >= 2) // Captures start at [1].
element.id = matches[1] + "_" + cur_num;
}
if(element.name)
{
var matches = element.name.match(/(.+)_\d+/);
if(matches && matches.length >= 2) // Captures start at [1].
element.name = matches[1] + "_" + cur_num;
}
});
$(cloned).appendTo( whereToPutIt );
});
});
The Markup:
<div id="smallConfig">
<div id="MainConfig">
<ul>
<li id="red_1">red</li>
<li id="blue_1">blue</li>
</ul>
<input id="purple" type="text" value="I'm a text box" name="textboxIsaid_1" />
</div>
</div>

FWIW, I used Dario's function, but needed to catch form labels as well.
Add another if statement like this to do so:
if(element.htmlFor){
var matches = element.htmlFor.match(/(.+)_\d+/);
if(matches && matches.length >= 2) // Captures start at [1].
element.htmlFor = matches[1] + "_" + cur_num;
}

If you will have several similar items on a page, it is best to use classes, not ids. That way you can apply styles to uls inside different container ids.

I believe this is the best way
var $clone = $("#MainConfig").clone(false);
$clone.removeAttr('id'); // remove id="MainConfig"
$clone.find('[id]').removeAttr('id'); // remove all other id attributes
$clone.appendTo($("#smallConfig")); // add to DOM.

Here is a solution with id.
var clone = $("#MainConfig").clone();
clone.find('[id]').each(function () { this.id = 'new_'+this.id });
$('#smallConfig').append(clone);
if you want dynamically set id, you can use counter instead of 'new_'.

Related

How to remove last element using jquery?

Here I'm trying to create a calling pad that reads a maximum of 10 numbers at a time, and displays the numbers as a maximum of 6 numbers in a row. It's working functionally. I want to remove the last number when the user presses the clear button.
I used $("#calling-pad").last().remove(); to try to remove the last number, but it removes the whole contents and doesn't allow to enter a new number. How can I fix it?
var key = 1;
$("#nine").click(function(){
if (p === 1) {
$("#mini-screen").css("display","none");
$("#number-screen").css("display","block");
if (key < 11) {
if ((key % 7) !== 0) {
$("#calling-pad").append("9");
key = key + 1;
}
else {
$("#calling-pad").append("<br>");
$("#calling-pad").append("9");
key = key + 1;
}
}
}
});
$("#inner-icon-one").click(function(){
if (p === 1) {
$("#mini-screen").css("display","none");
$("#number-screen").css("display","block");
if (key > 1) {
if ((key%6) !== 0) {
$("#calling-pad").last().remove();
key = key - 1;
if ( key === 1) {
$("#number-screen").css("display","none");
$("#mini-screen").css("display","block");
}
}
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="calling-pad"> </span>
You are just appending numbers to a span tag and are not really keeping track of user input.
$("#calling-pad").last().remove();
Is telling jQuery to remove the full contents because you are not inserting any child elements to the calling-pad span.
Therefore you could use an array to keep track of the users numbers or use a counter as I have shown below.
var totalInputs = 0;
$("#insert").on("click", function() {
totalInputs++;
var inputText = $("#input").val();
var id = "calling_" + totalInputs;
$("#calling-pad").append("<span id='" + id + "'>" + inputText + "</span>");
});
$("#remove").on("click", function() {
$("#calling_" + totalInputs).remove();
totalInputs--;
});
span {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type="text" id="input" />
<button id="insert">Insert</button>
<div id="calling-pad">
</div>
<button id="remove">Remove last element</button>
Problem - Using 'last' instead of ':last-child'
The jQuery last method does not find child elements. Instead, given a collection of elements matching a selector, it filters that collection to include only the last element. Combining this with an id-selector (i.e. $("#element-id").last()) is always redundant, since $("#element-id") only matches a single element, and the resulting jQuery object is always of size 1. If there's only one element, it's always the last one.
Therefore $("#calling-pad").last().remove(); is effectively the same as saying $("#calling-pad").remove();.
Solution
Instead, when you're appending data to the #calling-pad element, ensure they're included as new elements (e.g. wrapped in <span></span> tags):
$('#calling-pad').append("<span>9</span>");
Then, when you want to remove the last element in the #calling-pad, you simply have to do this:
$('#calling-pad > span:last-child').remove();
This finds all span elements that are direct children of the #calling-pad, filters that to only include the last element (using :last-child), and then removes that element.
$("#calling-pad").contents().last().remove();
if ($("#calling-pad").contents().last().is("br")) {
$("#calling-pad").contents().last().remove();
}
As you're dealing with textNodes, you need to use .contents() - the <br> split them up so no need to parse things, and if you're deleting the last node, you need to delete the last break at the same time...
You need one line to remove last comment... no need to count ids ...
here is snippet ... Cheers Man
$("#insert").on("click", function() {
var inputText = $("#input").val();
$("#calling-pad").append("<span>" + inputText + "</br></span>");
});
$("#remove").click(function(){
$("#calling-pad").children("span:last").remove()
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type="text" id="input" />
<button id="insert">Insert</button>
<div id="calling-pad">
</div>
<button id="remove">Remove last one</button>

Is there an index number to use when using jQuery upon matched set of DOM element?

I am wondering if there is an index number to use when using jQuery to manipulate DOM.
For example can I do something like
$("#elements a").css('id','element-'+index);
So to turn
<div id="elements">
</div>
to
<div id="elements">
<a id="element-1" href="#"></a>
<a id="element-2" href="#"></a>
<a id="element-3" href="#"></a>
</div>
if you want to set the id attribute then you need to user .attr()
$("#elements a").attr('id',function(index){
return 'element-'+ (index + 1)
});
Demo: Fiddle
Arun's method will work just fine, but so will this and it seems a little simpler in concept because .each() passes the callback the index of the item it's enumerating so you can use that directly to just assign the id value:
$("#elements a").each(function(index) {
this.id = 'element-'+ (index + 1);
});
Or, if you're trying to just reference the 3rd item without assigning an id ahead of time, you can do that like this:
$("#elements a").eq(2).css("color", "red");
do it like this..
var i=1;
$("#elements a").each(function(){
$(this).attr("id","element-"+i);
i++;
});
Are you trying to access a certain anchor element based on the last number in the ID?
If so, just loop through the elements returned by $("#elements").find('a') and test the ID against something. Like this:
var d = document.createElement('div');
var search = 1;
d.id = "elements";
d.innerHTML = '<a id="element-1" href="#"></a> <a id="element-2" href="#"></a> <a id="element-3" href="#"></a>';
$(d).find('a').each(function (index, item) {
if (parseInt(item.id.split('-')[1]) == search)
alert("got it");
});

How to make querySelectorAll select only from child elements of the current element

What I'm asking is how to implement the equivalent functionality of jQuery's children() with HTML5's querySelector/querySelectorAll, i.e. how do I designate the current element in the selector pattern.
For example:
<div id="foo">
<div class="bar" id="div1">
<div class="bar" id="div1.1">
</div>
</div>
<div class="bar" id="div2"></div>
</div>
With document.getElementById('foo').querySelectAll('div.bar') all three divs will be selected. What if I only wanna get div1 and div2, not div1's child div1.1? How do I write [[current node]] > div.bar like css selector?
Could anybody shed some light on this?
In your example you have did id="foo", so above example works.
But in a situation when parent element has no ID, but you still want to use querySelectorAll to get immediate children - it is also possible to use :scope to reference element like this:
var div1 = document.getElementById('div1'); //get child
var pdiv = div1.parentNode; //get parent wrapper
var list = pdiv.querySelectorAll(':scope > div.bar');
Here query will be "rooted" to pdiv element..
Actually there is a pseudo-class :scope to select the current element, however, it is not designated as being supported by any version of MS Internet Explorer.
document.getElementById('foo').querySelectAll(':scope>div.bar')
There's no selector for designating the element from which the .querySelectorAll was called (though I think something may have been proposed).
So you can't do anything like this:
var result = document.getElementById('foo').querySelectorAll('[[context]] > p.bar');
What you'd need would be to select from the document, and include the #foo ID in the selector.
var result = document.querySelectorAll("#foo > p.bar");
But if you must start with an element, one possibility would be to take its ID (assuming it has one) and concatenate it into the selector.
var result = document.querySelectorAll("#" + elem.id + " > p.bar");
If it's possible that the element doesn't have an ID, then you could temporarily give it one.
var origId = elem.id
if (!origId) {
do {
var id = "_" + Math.random().toString(16)
} while (document.getElementById(id));
elem.id = id;
}
var result = document.querySelectorAll("#" + elem.id + " > p.bar");
elem.id = origId;
You can just use like this:
document.querySelectorAll('#foo > p.bar')

Javascript - get text between <li> by ID

I'm trying to write some javascript that will grab the inner text of li elements (1-16) and put them into hidden fields.
var myValue9 = document.getElementById("fileName9").value;
oForm.elements["fileName9"].value = myValue9;
<input name="fileName9" type="hidden" id="fileName9" />
<li id="wavName9"> Some Text </li>
How do I return the text in between the <li> and put into the hidden field?
Simple JavaScript:
document.getElementById("fileName9").value = document.getElementById("wavName9").innerText;
You could, in this case, also use innerHTML but that would also give you the HTML the element contains.
LI tags don't have a .value property. Using plain javascript, you could do it this way:
oForm.elements["fileName9"].value = document.getElementById("wavName9").innerHTML;
Or, to do all of them from 1 to 16, you could use this loop:
for (var i = 1; i <= 16; i++) {
oForm.elements["fileName" + i].value = document.getElementById("wavName" + i).innerHTML;
}
Or since you also tagged your post for jQuery, using jQuery you could do it like this:
$("#fileName9").val($("#wavName9").text());
Or, to do all of them from 1 to 16:
for (var i = 1; i <= 16; i++) {
$("#fileName" + i).val($("#wavName" + i).text());
}
Use jQuery to do it.
var myvar = $("#wavName9").html()
I think this will do in for all li's
$("li[id^=wavName]").each(function(){
var $this = $(this);
$this.closest("input[id^=fileName]").val($this.text())
});
Create your li's with id's following such a structure: listitem-n, where n is 1-16 and input fields following the same structure hiddeninputs-n (n = 1-16)
using jfriend00's code, add it in a loop that will traverse 16 times, incrementing a count variable that you will use to transfer the data from list items to hidden inputs
var count = 0;
for( i=0; i < 16; i++){
count ++;
$("form #hiddeninput-"+count).val($("#listitem-"+count).text());
}
Better validate the code, but there's the general idea.
You could also create the hidden fields in javascript from scratch, which would make the code abit more stable IMO as there's less chances of a hidden field missing in the form when the js is executed.
Using jQuery:
$('#fileName9').val($('#wavName9').text());
Note that you can change .text() to .html() to return the HTML structure rather than just the text.
You could automate this for multiple <li>'s like so:
$('li[id^="wavName"]').each(function () {
var number = this.id.replace('waveName', '');
$('#fileName' + number).val($(this).text());
});
This selects all <li>'s who's id starts with "wavName" and stores the text within the <li> tag in the hidden input who's id starts with "fileName" and ends with the same integer as the <li> tag.

Use jQuery/Javascript to create a list of all elements in a set

I want to use Javascript to loop through a set of elements, and create a list of labels for each one, so if the set of elements were as follows:
<h1>Title</h1>
<h2>Subheading</h2>
<p>Paragraph of text...</p>
It would give me the following:
<ol>
<li>h1</li>
<li>h2</li>
<li>p</p>
<ol>
Is it possible for jQuery/Javascript to return an element's type as a string, and if so how would I go about it?
This is far from the cleanest piece of code I've ever done, but it works:
function generateList(rootElement) {
var rootElementItem = $("<li>" + rootElement.get(0).tagName + "</li>");
if (rootElement.children().size() > 0) {
var list = $("<ol />");
rootElement.children().each(function() {
list.append(generateList($(this)));
});
rootElementItem.append(list);
}
return rootElementItem;
}
var listOfElements = generateList($("body"));
$("body").append($("<ol/>").append(listOfElements));
Demo: http://jsfiddle.net/jonathon/JvQKz/
It builds upon the this.tagName answer that was previously given, but it checks for children also. This way it will build up a hierarchical view of the element given. The method doesn't generate the enclosing <ol/> tag so that it can just be appended to an existing one.
I hope this simple solution helps:
http://jsfiddle.net/neopreneur/n7xJC/
-html-
<h1>Title</h1>
<h2>Subheading</h2>
<p>Paragraph of text...</p>
-js-
$(function(){
$('body *').each(function(){
// append a 'ol' to body in order to output all tag names
if(!$(this).parents('body').find('ol').length){
$('body').append('<ol/>');
}
// output the name of the tag (list item)
$('ol').append('<li>' + this.tagName.toLowerCase() + '</li>');
});
});
This works assuming you have a properly formed HTML document with a body tag.
example
$('<ol />').append(function(index, html){
var ret = $('<span />');
$('body>*').each(function(i, el) {
ret.append( $('<li />').html(this.tagName.toLowerCase()) );
});
return ret.html();
}).appendTo('body');

Categories

Resources