Scenario is to copy #first inside #test, below are the 2 scenarios of implementing it and which is the best way of implementation and why?
<div class="first">
<div class="second">1</div>
</div>
<div class="test">
<div>--------------</div>
</div>
JQUERY1:
var cloner = $('.first').clone().prop({
'class': 'changed_first'
});
$('.test').append(cloner)
$('.changed_first > .second').attr('class', 'changed_second');
$('.changed_first > .second').html('2');
Detour question on JQUERY1: Is there a possibility in the clone method to change the properties of inner elements?
JQUERY2:
$('.test').append('<div class="changed_first"><div class="changed_second">2</div></div>');
your 1st method of using clone will be a good one.
And yes you can manipulate the cloned elements before you bind it to dom.
If you want to access any id or class to cloned element before you bind if to anywhere, you can do like
var cloner = $('.first').clone().prop({
'class': 'changed_first'
});
cloner.find('#id').css('something','css-value');
var data_id = cloner.find('.class').attr('data-bind');
Related
How can I get the the Nth parent of multiple elements?
Right now I do it like this:
var winners = THUMBNAILS.find(".tournament-winner:contains(" + filter + ")");
var filteredThumbnails = winners.parent().parent().parent().parent().clone();
this does work.
But when I try to do it like:
var filteredThumbnails = winners.parents().eq(3).clone();
It only gets the thumbnail (great grandfather) of just one element in the winners variable.
Is there any easier way to get Nth parent of multiple elements?
You can use .add()
Create a new jQuery object with elements added to the set of matched elements.
//Create empty object
var filteredThumbnails = $();
//Iterate and target parent
winners.each(function(){
filteredThumbnails.add($(this).parents().eq(3).clone());
});
write a .map() or .forEach() function on the winners array.
example:
filteredThumbnails = winners.map(winner => winner.parent().parent().parent().parent().clone());
It would be a lot easier if you shared your html.
As i am not sure what you are trying to achieve...but
Another way of getting up multiple parent levels of the element you are targeting,
which doesn't have a unique name would be like below. which will traverse up until it finds a parent which has an id which starts with "parent".
winners.parents("[id^=parent]")
Although i would give them a generic css class to travel up to rather than this.
You can also use filter on return value of parents.
$.parents return an array.
Let's say you have the following HTML and you want to get divs with ids parentN. which is the third parent. After that you can convert the elements to an array. Alternatively, you can create a function like this and use it in different places.
then you have to get every fourth element of the parents
function getNthParents(obj, N) {
return obj.parents().filter(index => !((index + 1) % N)).toArray();
}
nthParents = getNthParents($('.thumbnail'), 3)
console.log(nthParents)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='parent1'>
<div>
<div>
<div class='thumbnail'>
</div>
</div>
</div>
</div>
<div id='parent2'>
<div>
<div>
<div class='thumbnail'>
</div>
</div>
</div>
</div>
<div id='parent3'>
<div>
<div>
<div class='thumbnail'>
</div>
</div>
</div>
</div>
I want to add additional html in the cloned object.
var item = $("#clone")
.clone(true, true)
.attr({"id": "citem", "class": "row cartItem_" + item_id})
.css('display', 'block')
.appendTo("#all-items");
I know about wrap method but that is something else. I want to append html after this cloned object. Or somehow i can manipulate the HTML of the cloned object element.
This approach is to explain how the .clone() works, and covers all the states you ever mentioned in the question, such as..
Creating a clone of a DOM
Appending additional raw HTML to a clone
Manipulating the clone
Manipulating the content in the clone
Clone in another clone
Appending another clone to a clone
Appending HTML after this cloned object
$(function() {
//! Cloning the HTML
var $clonedContent = $('.content').clone();
// Manipulate the cloned element
// -- Update the existing content
$clonedContent.find('h5').text("My content just got manipulated");
// -- Adding raw HTML content
$clonedContent.append("<small> It's a raw HTML </small>");
// -- Adding property to an existing content
$clonedContent.find('small').addClass('make-me-day');
//! Getting another cloned content
var $anotherClonedContent = $('.content').clone();
// -- Another manipulation of another cloned content
$anotherClonedContent.find('h5').text("This is another cloned content");
// -- Manipulate the another cloned content's content
$anotherClonedContent.find('h5').addClass('make-me-day');
// -- Add another cloned content to the already manipulated & cloned content.
$clonedContent.append($anotherClonedContent);
//! Finally, add the clonedContent to the DOM, aaaand.. add more HTML afterwards.
$('#main').append($clonedContent, "<em> I added this HTML after the cloned object </em>");
});
.make-me-day {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
<div class="content">
<h5> Just a simple content </h5>
</div>
</div>
Assuming you are trying to add html after the clone:
$("#toclone")
.clone()
.attr({"id":"cloned"})
.appendTo("#all-items")
.after("<div>some more content <em>after</em> the clone</div>");
The .appendTo() returns the element that was appended, so you can then manipulate it as required, eg using .after()
I think that's more easy than you imagine:
$(function(){
var item_id=0;
// function to clone your element
var newItem=function(){
item_id++;
return $('#clone')
.clone(true, true)
.attr({'id':'citem_'+item_id, 'class':'row cartItem_'+item_id})
.css('display','block')
.appendTo('#all-items');
};
// Clone element and edit what you want
newItem().html('hobby').css('color','blue');
// Clone element and append what you want
newItem().append(' - <i>spaghetti</i>');
// You can also find element by id
$('#citem_2').css('color','red');
//You can add buttons to do it
$('button:eq(0)').on('click',function(){
newItem().html('Your <b>html</b> here.');
});
$('button:eq(1)').on('click',function(){
newItem().append(' - Your <b>html</b> here.');
});
});
<button>New clone</button>
<button>New clone + append</button>
<div id="all-items">
<div id="clone">pizza</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
I took a close look to all answers and comments to this bounty question...
I can see that the bidder is kind of demanding, which is okay since 100 rep. points is valuable.
I think that the question contains two, in fact.
How to clone
How to «manipulate the HTML of the cloned object» - Wasif Iqbal on Sep 22th.
I think the question is intended to get explanations on how to manipulate the clone, not only on creation and appending to somewhere, but also afterward.
I really think my very cool example below could be a «valid answer» - Vixed on Sep 29th.
The other answers were good too, anyway... So a made a supplemental effort. ;)
First explanation of all:
Cloning an element is done by jQuery .clone(). So have a nice reading.
Then:
jQuery chaining is nice to append some other stuff «inside» or «before/after» the clone in a concise way, as demonstrated in other answers.
But to manipulate it afterward, like in another click event handler...
This is the trick to know, which is not explained in the previous reference:
You have to make sure to set a unique id attribute to it, instead of the same id as the original.
Because you know that an id shall be unique!
«One ring to rule them all.
One ring to find them, one ring to bring them all and in the darkness bind them.»
- A well known deamon said this while forging a curse...
Then... What more explanation could I give if it ain't clear?
Alert reader should have understood everything already.
I made a funny «clone-and-kill-it-game» to demontrate cloning and further manipulations.
For the «inspiration», I have to admit that I saw a japaneese zombie movie yesterday night...
lol!
Have fun with this code snippet:
(also on CodePen)
// Constants
var number = 1;
var revealed = false;
// The cloning function
$("#cloneIt").click(function(){
var cloning = $("#Human")
.clone()
.attr({"id": "Clone_number_"+number, "class":"clone"})
.appendTo("#CloneBucket");
$(this).val("Clone him again! It's fun!");
number++;
if(number==4){
$(".reveal").show();
}
if(number==9){
$(this).val("Enought! This is now illegal.").prop("disabled",true);
}
// Add them to select
var options="<option disabled selected class='deceased'>KILL THEM!</option>";
for (i=1;i<number;i++){
if( $("#CloneBucket").children().eq(i-1).hasClass("dead") ){
options += "<option value='"+i+"' class='deceased'>Clone #"+i+"</option>";
}else{
options += "<option value='"+i+"'>Clone #"+i+"</option>";
}
}
$("#cloneSelect").html(options);
if(revealed){
reveal(); // Sub function to add clones to a select element.
}
});
// Reveal clone numbers
$(".reveal").click(function(){
reveal();
setTimeout(function(){
$(".reveal").val("Kill a clone! (While it's not illegal!)").removeClass("reveal").addClass("shoot");
},50);
});
// Kill button
$("#page").on("click",".shoot",function(){
$(this).prop("disabled",true).val("Select one");
$("#cloneSelect").show();
});
// Select kill target
$("#cloneSelect").change(function(){
var thisCloneIs = parseInt($(this).val());
var niceShot = "#Clone_number_"+thisCloneIs;
$(niceShot).css({"opacity":0.3,"color":"red"});
$(niceShot+" .definition").html("I was the number"+thisCloneIs).parent().addClass("dead");
// Redish the option
$(this).find("option").eq(thisCloneIs).prop("disabled",true).addClass("deceased");
$(this).find("option").eq(0).prop("selected",true);
// Bravo!
var allDead = [];
setTimeout(function(){
$("#cloneSelect").find("option").each(function(index){
if( $("#cloneSelect").find("option").eq(index).hasClass("deceased") ){
allDead.push(true);
}else{
allDead.push(false);
}
});
if( allDead.indexOf(false)==-1 ){
// Reset this super gaming experience for a new.
$("#CloneBucket").html("");
$(".shoot").addClass("reveal").removeClass("shoot").val("Reveal clone numbers!").prop("disabled",false).hide();
$("#cloneIt").val("Clone again?").prop("disabled",false);
$("#cloneSelect").html("").hide();
revealed = false;
number = 1;
}
},50);
});
function reveal(){
$(".clone .definition").each(function(index){
var cloneIndex = index+1; // zero-based
$(this).html("I'm the number "+cloneIndex);
revealed = true;
});
}
img{
width:60px;
}
div{
text-align:center;
}
.reveal{
display:none;
}
#CloneBucket div{
display:inline-block;
padding:10px;
}
#CloneBucket{
margin:0 auto;
text-align:center;
}
select{
display:none;
margin:0 auto;
}
.deceased{
color:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<div id="page">
<input type="button" id="cloneIt" value="Want to clone him?"><br>
<br>
<div id="Human">
<img src="http://image.flaticon.com/icons/svg/10/10522.svg"><br>
<span class="definition">I'm a real human!</span>
</div>
<br>
<input type="button" class="reveal" value="Reveal clone numbers!">
<select id="cloneSelect"></select>
<div id="CloneBucket"></div>
<br>
</div>
Still waiting for clarification in the comments, but I think this solution is what you are looking for:
$('button').click(function() {
$('#clone').clone()
.append('<span style="color:red;">some other elements</span>')
.removeAttr('id')
.appendTo('#all-items');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Click to clone</button>
<div id="all-items">
<div id="clone">pizza</div>
</div>
Since the appendTo returns the original element that was appended, you can use after on the returned value to add some new element after the cloned element that you just appended:
$('button').click(function() {
$('#clone').clone()
.append('<span style="color:red;">some other elements</span>')
.removeAttr('id')
.addClass('cloned')
.appendTo('#all-items')
.after('<div>this element was added after the cloned element (no blue border here)</div>');
});
.cloned {
border: 1px solid blue;
margin: 5px;
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Click to clone</button>
<div id="all-items">
<div id="clone">pizza</div>
</div>
One can add to a collection at any time using jQuery's add() function.
This effectively adds to the collection, placing whatever is passed to add() after the clone itself, as opposed to append which places the content inside the clone, answering the question
"I want to append html after this cloned object"
var more1 = $('<span />', {html : '<em> and will</em>'}); // element(s) to add
var more2 = '<span> happen again....</span>'; // or strings of HTML for that matter
var item = $("#clone").clone(true, true)
.attr({"id": "citem"})
.show()
.add(more1) // add whatever after the clone
.add(more2) // before appending everything
.appendTo("#all-items");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="clone">
<span>All of this has happened before</span>
</span>
<br /><br /><br />
<div id="all-items"><!-- clone goes here --></div>
From the documentation
Given a jQuery object that represents a set of DOM elements, the
.add() method constructs a new jQuery object from the union of those
elements and the ones passed into the method.
The argument to .add()
can be pretty much anything that $() accepts, including a jQuery
selector expression, references to DOM elements, or an HTML snippet.
example : $("p").clone().add("<span>Again</span>").appendTo(document.body);
add() does not change the original collection, but returns a new collection, so if not chaining directly on the modified collection, one has to store that collection
var clone = $('#elem').clone(true, true);
var changed = clone.add('<div>new content</div>'); // clone is not changed
Manipulating the content inside a clone is done in the exact same way as manipulating any other collection with jQuery
Post something link this
var clone = parent.find('.divclone').clone();
clone.removeClass('identifier');
clone.removeClass('hide');
//.. code changes for the new clone
clone.find(".link-slug").attr('href',invstr_val.slug_url);
// append it again to the original
clone.insertAfter(parent.find(".divclone"));
I have the following code:
.recipe
.ingredients
= f.simple_fields_for :ingredients do |ingredient|
= render 'ingredient_fields', f: ingredient
.row#links
.col-xs-12
= link_to_add_association "", f, :ingredients
%hr
I need to select the ingredients div using jquery in the format of $("#links")["closest"](".recipe > .ingredients") but this doesn't select anything.
It's frustrating though as $("#links")["closest"](".recipe > .row") will return the correct div.
Fiddle of what works and what I want: https://jsfiddle.net/yL6dr4s1/
According to jQuery documentation, closest method tries to find element matching the selector by testing the element itself and
traversing up through DOM.
It does not go through siblings of the element.
Based on your requirements, it seems like you want to traverse the tree for getting match in siblings. jQuery has siblings method to do that. So one solution would be to use siblings method like:
$("#links")["siblings"](".recipe > .ingredients")
Another soultion would be to get closest parent and then use children as answered by #mhodges
As for the query $("#links")["closest"](".recipe > .row"):
It works fine because closest method finds the match in the element itself.
Here is the example to showcase that:
$(document).ready(function() {
// Match found because it is parent
console.log($("#links")["closest"](".wrapper").length);
// No match found because element is sibling
console.log($("#links")["closest"](".row1").length);
// No match found because element is sibling
console.log($("#links")["closest"](".row3").length);
// Match found because it is element itself
console.log($("#links")["closest"](".row2").length);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="row1">
<span>Content1</span>
</div>
<div class="row2" id="links">
<span>Content2</span>
</div>
<div class="row3">
<span>Content3</span>
</div>
</div>
I am not sure of your requirements on using the exact selector/syntax you provided, but this selector works exactly how you want it to.
$(this).closest(".recipe").children(".ingredients").append('<br/><input type="text" value="Flour">');
Edit
This is the closest I could get:
$(this)["closest"](".recipe").children(".ingredients").append('<br/><input type="text" value="Flour">');
I don't think you can use the selectors in the way you propose.
As far as the DOM is concerned (and jQuery), the element defined by ingredient and the element defined by row are not related. You have to traverse up to the parent element, then back down to get to the child.
Here is a fiddle that hopefully demonstrates the issue.
If you can change it so that ingredient and row are both within the same parent div, you might have more luck with your test selector syntax.
When jQuery gets to buggy, doesn't have a certain option or just becomes to messy to use for a certain operation, it is good we also have access to good old plain javascript.
document.querySelector('#addToIngredients').addEventListener('click' , function(e) {
var recipe = getClosest(e.target,'recipe');
if (recipe) {
var ingred = recipe.querySelector('.ingredients');
ingred.innerHTML += '<br/><input type="text" value="Flour">';
}
});
function getClosest(elem,cls) {
var el = elem.parentNode;
while (el){
if (el.className.indexOf(cls) > -1) {
return el;
}
el = el.parentNode;
}
return false;
}
<div class="recipe">
<div class="ingredients">
<input type="text" value="Eggs"><br/>
<input type="text" value="Flour">
</div>
<div class="row">
<div class="col-xs-12">
Add to .ingredients
</div>
</div>
<hr/>
</div>
Of course they can be combined
$(function() {
$("#addToIngredients").on('click', function(e) {
var recipe = getClosest(e.target,'recipe');
if (recipe) {
var ingred = recipe.querySelector('.ingredients');
ingred.innerHTML += '<br/><input type="text" value="Flour">';
}
});
})
HTML:
<div class="my_1"></div>
<div class="my_big">
<div class="small" id="id_1"></div>
<div class="small" id="id_2"></div>
<div class="small" id="id_3"></div>
<div class="small" id="id_4"></div>
</div>
javascript:
var css_scope=$(".my_big");
var next_div=$(".my_1").nextAll('.small',css_scope).first();
console.log(" \n next div = "+ next_div.attr('id'));
console shows undefined. But if I exclude the my_big div from html and define var next_div in javascript as follows:
var next_div=$(".my_1").nextAll('.small').first();,
expected output is obtained.
How to make nextAll() work with the mentioned css scoping ?
.nextAll is used to find all the next siblings, you should find the .small from the result of nextAll.
var next_div=$(".my_1").nextAll('.my_big').find('.small').first();
You cannot get to .small from my_1 using nextAll() since they are not siblings. You can get to it using the following selector.
// Get the first element matching ".small" inside an element matching ".my_big"
// that comes immediately after an element matching ".my_1"
var next_div = $('.my_1 + .my_big > .small:first');
Check this demo: http://jsfiddle.net/q6XKR/
If you want to access the first element in my_big div, there's no need to bring my_1 into the scene.
var next_div = $('.my_big').find('.small').first();
console.log(" \n next div = "+ next_div.attr('id'));
Hope it clarifies you somewhat about traversing elements in jQuery.
Let's say i need to find all .bar elements inside an element that's assigned to a variable foo.
In jQuery, foo.find('.bar') solves the problem.
What's the equivalent function in mooTools?
<div id="container">
<span class="elems">...</span>
<span class="elems">...</span>
...
</div>
----
var elems = $('container').getElements('.elems');
// elems is now an array containing all spans with the class 'elem'
Or:
var elems = $$('#container.elems');