The following code should clone the given element and insert it after:
function cloneMore(element) {
var newElement = element.cloneNode(true);
element.parentNode.insertBefore(newElement, element.nextSibling);
}
var addChoice = document.getElementById("add-choice")
addChoice.onclick = (function(){cloneMore(document.getElementById("choices").lastChild);})
The html looks like this:
<ul id="choices">
<!-- form elements -->
</ul>
<p><a id="add-choice">Add another choice</a></p>
No exception is thrown and everything executes but I can not see the new element. Why?
lastChild picks all node types, not only Elements. So you are probably cloning a TextNode \n.
I guess that what you want is lastElementChild.
function cloneMore(element) {
var newElement = element.cloneNode(true);
element.parentNode.insertBefore(newElement, element.nextSibling);
}
var addChoice = document.getElementById("add-choice")
addChoice.onclick = function() {
var choices = document.getElementById("choices");
cloneMore(choices.lastElementChild); // only Elements
console.log('lastChild type : ', choices.lastChild.nodeName);
}
<ul id="choices">
<li> choice </li>
</ul>
<p>Add another choice</p>
Related
I have a test project where I am using Firebase as a way to render data on a HTML page. It works fine but I need to include more nested HTML (divs) with classes to maintain my layout. Is it possible to do this? I have following Javascript.
const productList = document.querySelector('#productList');
function renderProducts(doc) {
let li = document.createElement('li');
let divInner = document.createElement('div');
let title = document.createElement('h2');
li.setAttribute('data-id', doc.id);
title.textContent = doc.data().title;
li.appendChild(divInner);
li.appendChild(title);
div.setAttribute('class', 'inner');
document.body.appendChild(div);
productList.appendChild(li);
}
db.collection('Products').get().then((snapshot) => {
snapshot.docs.forEach(doc => {
renderProducts(doc);
})
})
However, this produces the following HTML, where obvisously nothing is nested. *Note I have removed the data for the purpose of explaining.
<ul id="productsList">
<li>
<div class="inner"></div>
<h2></h2>
</li>
</ul>
However, my desired outcome would be to then somehow manipulate the DOM to move the <h2> inside the <div>;
<ul id="productsList">
<li>
<div class="inner">
<h2></h2>
</div>
</li>
</ul>
Looks like you are appending it in wrong place.
Please try this one.
function renderProducts(doc) {
let li = document.createElement('li');
let divInner = document.createElement('div');
let title = document.createElement('h2');
li.setAttribute('data-id', doc.id);
title.textContent = doc.data().title;
divInner.appendChild(title);
li.appendChild(divInner);
div.setAttribute('class', 'inner');
document.body.appendChild(div);
productList.appendChild(li);
}
I'm trying to learn HTML and Javascript/jQuery. If I have a container which holds a title, an image, a description and a number, then I want to create a new container with the exact same format (except the values will be different), how is this commonly done?
This is an example of the format I'm looking for in each item.
<li>
<div>
<div>
Image Name
</div>
<div>
<a href=URL>
<img src='image_url'>
</a>
</div>
<div>
Description
</div>
<div>
num_comment Comments
</div>
</div>
</li>
Do I just create a string and concatenate with the actual values for the image, then add that string to some variable I've saved called html_content, and then set the html value to html_content? Is that the common way of doing this or is there a better way?
EDIT
To give a better idea of what I'm currently doing, here's the javascript:
var html1 = '<li><div><div>';
var html2 = '</div><div><a href="';
var html3 = '"><img src="';
var html4 = '"></a></div><div>';
var html5 = '</div><div>';
var html6 = '</div></div></li>';
function render(pics){
for (var i in pics){
html = html + html1 + pics[i].name + html2 + pics[i].image_url + html3 + ...
};
$('pics').html(html);
}
In jQuery you just have to use the append() function to add on to something.
You could do something like...
$('select element').append('<li><div>....etc.');
and where you want a different value you can use a variable.
You can use .clone() and create a copy of this, then iterate through the cloned object and change what you need:
var $objClone = $("li").clone(true);
$objClone.find("*").each(function() {
//iterates over every element. customize this to find elements you need.
});
To change the image source you can do:
$objClone.find("img").attr("src", "new/img/here.jpg");
Fiddle demoing the concept: http://jsfiddle.net/H9DnA/1/
You may find it useful to explore some of the JavaScript templating libraries. The essential idea is that you create a template of your markup:
<li>
<div>
<div>
{{name}}
</div>
<div>
<a href="{{url}}">
<img src="{{imageUrl}}">
</a>
</div>
<div>
{{description}}
</div>
<div>
{{comments}}
</div>
</div>
</li>
Then you merge it against some associated matching object and insert it into your document:
{ name: 'Image Name',
url: 'http://example.com',
imageUrl: 'http://example.com/image.jpg',
description: 'Description',
comments [ { text: 'Comment' } ]
}
function render(pics)
{
var theList = document.getElementByid("LIST ID");
for (var i in pics){
var listItem = document.createElement('li'); // Create new list item
var nameDiv = document.createElement('div'); // Create name DIV element
nameDiv.innerHTML = pics[i].name; // Insert the name in the div
var img = document.createElement('img'); // Create Img element
img.setAttribute('src',pics[i].src); // Assign the src attribute of your img
var imgDiv = document.createElement('div'); // Create Img Div that contains your img
imgDiv.appendChild(img); // Puts img inside the img DIV container
var descDiv = document.createElement('div'); // Create Description DIV
descDiv.innerHTML = pics[i].description; // Insert your description
listItem.appendChild(nameDiv); // Insert all of you DIVs
listItem.appendChild(imgDiv); // inside your list item
listItem.appendChild(descDiv); // with appropriate order.
theList.appendChild(listItem); // Insert the list item inside your list.
}
}
I think this will work just fine:
$('#button').click(function () {
var html1 = '<li><div><div>';
var html2 = '</div><div><a href="';
var html3 = '"><img src="';
var html4 = '"></a></div><div>';
var html5 = '</div><div>';
var html6 = '</div></div></li>';
function render(pics){
for (var i in pics){
html = html + html1 + pics[i].name + html2 + pics[i].image_url + html3 + ...
$("ul").append(html);
}
}
// call render
});
I didn't do a test run on your code so there might be an error somewhere. My tweak adds this line $("ul").append(html); inside your loop
I need your help to solve a problem I have.
I have this code:
<div id="div1" >
<div id="edit1">
hello
<input type="button" id="b1" onclick="aaa()"/>
</div>
</div>
I want to use insert into the internal div (id=edit1) another new div I generated.
I tried alike code but it's not running:
js:
function aaa()
{
var elem = createDivLine();
var el1 = document.getElementById("div1");
var el2 = el1.getElementById("edit1");
el2.appendChild(elem);
}
function createDivLine()
{
var tempDiv1 = document.createElement("div");
tempDiv1.innerHTML = "Sam";
return tempDiv1;
}
The result should looks like this:
<div id="div1" >
<div id="edit1">
hello
<input type="button" id="b1" onclick="createDivTable()"/>
<div>"Sam"</div>
</div>
</div>
JSFiddle: http://jsfiddle.net/KknXF/
Since IDs are unique, it is not valid to attempt to get an element's children by ID.
Remove this line:
var el1 = document.getElementById('div1');
And change the following line to:
var el2 = document.getElementById('edit1');
In the event that you have some irrepairably (I can never spell that word...) broken HTML that you can't possibly change, try this:
var el2 = document.querySelector("#div1 #edit1");
It should be
function aaa() {
var elem = createDivLine();
var el2 = document.getElementById("edit1");
el2.appendChild(elem);
}
Demo: Fiddle
If I have two elements :
Element A :
<div id="myID">
<div id="a"></div>
<div id="b"><div id="ba"></div></div>
<div id="c"><span id="ca"></span></div>
</div>
and Element B :
<div id="myID">
<div id="a"></div>
<div id="b"><div id="ba"></div></div>
<div id="c"><span id="ca"></span></div>
<div id="d"></div>
</div>
Is it possible to find out that Element B has more children than Element A, then find where is additional element and create it in Element A?
P.S: In real code new element is loaded with Ajax Request, but I don't want to replace all content with loaded content, I need to add only new content and skip existing one.
P.S.S : In my current code I have Md5 checksum to check if new content is not the same as existing, but if new content have only little changes it replaces all content and this is the problem for me.
A piece of my current code :
window.processResponse = function(data) {
// Note : "data" is Ajax responseText;
if(!data) return false;
var $data = document.createElement("div");
$data.innerHTML = data;
var em = $data.getElementsByTagName("*");
for(var i = 0; i < em.length; i++)
{
var parent = sget(em[i].id); // sget function is : document.getElementById
if(parent)
{
var html = em[i].innerHTML.replace(/(\s)+/gim, "").replace(/(\n|\r\n)+/gim, "");
var id = em[i].id;
savedPages[id] = savedPages[id] || [];
var _md5 = md5(html);
if(savedPages[id][0] == _md5) continue;
savedPages[id] = [_md5, getTime()];
parent.innerHTML = em[i].innerHTML;
}
if(em[i].tagName === "SCRIPT")
{
var code = em[i].innerHTML.replace(/(\s)+/gim, "").replace(/(\n|\r\n)+/gim, "");
var id = em[i].id;
savedPages[id] = savedPages[id] || [];
var _md5 = md5(code);
if(savedPages[id][0] == _md5) continue;
savedPages[id] = [_md5, getTime()];
try{eval(em[i].innerHTML)}catch(ex){log(ex)};
}
}
};
So, you can optimize it but it depends also in which browser are you running this code.
I assumed the follows:
All IDs are unique, and you rely on that. You want to compare basically elements that have the same ID, not the same structure.
As you said, all children have IDs, and you want to compare only children – not nested node
The elements received from the server have only additional children not less. For removing children, you have to add some other code.
Therefore, if you have the same number of children, we assume they're the same (to optimize). If this is not true, then you have to implement the removal of children as well
Said that, I believe that this kind of stuff is more suitable on server side, that should send to the client only the part that are actually modified. That what we do usually – or, if we don't care, replace everything.
var div = document.createElement('div');
div.innerHTML = s;
var root = div.firstChild;
var children = root.children;
var documentRoot = document.getElementById(root.id);
if (documentRoot && documentRoot.children.length < children.length) {
var node = null;
var previousNode = null;
var index = 0;
while ( node = children[index++] ) {
var documentNode = document.getElementById(node.id);
if (!documentNode) {
if (previousNode)
documentRoot.insertBefore(node, previousNode.nextSibling);
else
documentRoot.insertBefore(node, documentRoot.firstChild);
documentNode = node;
}
previousNode = documentNode;
}
previousNode = null;
} else {
// probably append as is somewhere
}
The solution is not so simple. What if the parent, myID, did not exist in sample A but the child nodes were in both samples indicating 3 layers in the DOM that need to be compared and adjusted? How would you compare this:
<div id="papa">
<div id="myID">
<div id="a"></div>
<div id="b">
<div id="ba"></div>
</div>
<div id="c">
<span id="ca"></span>
</div>
</div>
</div>
vs
<div id="papa">
<div id="a"></div>
<div id="b">
<div id="ba"></div>
</div>
<div id="c">
<span id="ca"></span>
</div>
</div>
In this case the comparison becomes more complicated. You will actually need a fully fleshed out XML/HTML language aware diff utility with a merge function. You can play around with Pretty Diff to demonstrate just how complicated this can get, but unfortunately it does not have a merge function so it cannot be a fully automated solution to your problem.
I want my code to instead of creating two texts with my newly created span, i want it to say"Some text x2" and then x3 and so on.
Heres my code
<div>
<li id="myLi">
Some text (This is where i want my other text to be instead)
</li>
</div>
<td class="add" onmousedown="myFunction()">Add</td>
When i click the td, it adds to the li but when i click several times it just comes more text. I want it to say "Some text x2" instead.
function myFunction() {
var proc = document.createElement("SPAN");
var t = document.createTextNode("Some new text.");
proc.appendChild(t);
document.getElementById("myLi").appendChild(proc);
}
Thanks
As Mike said, you can do this with an innerHTML.
If I understand, what you want is :
var i =0;
function doStuff(){
var proc = "<span> my text instead x"+i + "</span>" ;
document.getElementById("myLi").innerHTML = proc;
i++;
}
<div>
<li id="myLi">
<p> something </p>
</li>
<div>
<button onclick="doStuff()"> CLICK ME </button>
How about this piece?
var globalCounter = 1;
function myFunction(){
var current = document.getElementById("myLi");
current.innerHTML = "Some Text x"+globalCounter;
globalCounter++;
}
http://jsbin.com/munukadama/edit?html,js,output
Note you will be using global counter. If you want to avoid global conflicts, either come up with unique variable name, or encapsulate within a class as a private variable (see below).
function MyClass(){
var counter = 1;
this.update = function(){
var current = document.getElementById("myLi");
current.innerHTML = "Some Text x"+counter;
counter++;
};
}
var myInstance = new MyClass();
And then button will become:
<button onClick="myInstance.update()">Click me for Class!</button>
Here is a jQuery solution & a jsfiddle to test it out with:
HTML:
<ul>
<li id="myLi">
Some text (This is where i want my other text to be instead)
</li>
</ul>
Add
JavaScript:
function myFunction() {
$('#myLi').html('<span>Some new text.</span>');
}
$('.add').on('click', myFunction);