AppendChild in for loop adds every second child? - javascript

I need to wrap children element in wrap-of-parent. Before wraping add some attributes to parent and child elements. In the code described below, everything works well if the children are one below the other in a separate row, and if they are in one row, every other child is inserted into the wrap-of-parent. Why is this happening and how to fix it?
I get:
<div id="container">
<div id="parent1">
<div id="child1"></div>
<div id="child2"></div>
<div id="wrap-of-parent1">
<div id="child3"></div>
<div id="child4"></div>
</div>
</div>
I need to get:
<div id="container">
<div id="parent1">
<div id="wrap-of-parent1">
<div id="child1"></div>
<div id="child2"></div>
<div id="child3"></div>
<div id="child4"></div>
</div>
</div>
Code:
var container = document.getElementById("container");
const parentDivs = container.querySelectorAll(":scope *"); //if child elements also have child elements, to wrap in
for (let parent of parentDivs) {
// create a new div
let wrap = document.createElement('div');
wrap.id = 'wrap-of-' + parent.id;
// move the parent's children to it
let children = parent.childNodes;
for (let i = 0; i < children.length; i++) {
if (children[i].nodeType === Node.ELEMENT_NODE) {
children[i].setAttribute("data", "somedata");
wrap.append(children[i]);
}}
// and append it to the parent
parent.appendChild(wrap);
}
<div id="container">
<div id="parent1">
<div id="child1"></div><div id="child2"></div><div id="child3"></div><div id="child4"></div>
</div>
</div>
<! - If the child elements are one below the other in a separate row it works, if they are in one row it does not work ->

If you keep moving the first (index 0) entry, it will work. You need to add an offset when the wrong nodeType is encountered to skip over those ones, and if there are no child nodes then don't process the node:
var container = document.getElementById("container");
const parentDivs = container.querySelectorAll(":scope *"); //if child elements also have child elements, to wrap in
for (let parent of parentDivs) {
// create a new div
if (parent.childNodes.length > 0) {
let wrap = document.createElement('div');
wrap.id = 'wrap-of-' + parent.id;
// move the parent's children to it
let children = parent.childNodes;
const nChildren = children.length;
let offset = 0;
for (let i = 0; i < nChildren; i++) {
if (children[offset].nodeType === Node.ELEMENT_NODE) {
children[offset].setAttribute("data", "somedata");
wrap.append(children[offset]);
} else {
offset++;
}
}
// and append it to the parent
parent.appendChild(wrap);
}
}
<div id="container">
<div id="parent1">
<div id="child1"></div><div id="child2"></div><div id="child3"></div><div id="child4"></div>
</div>
</div>

Related

Hide parent div when it contains <textarea>?

I have got a div structure that is dynamically generated by it's content. It is looking like this:
<div class="fpd-list-row fpd-add-layer" id="1609962837979"><div class="fpd-cell-0"><span></span></div><div class="fpd-cell-1">Dein Foto</div><div class="fpd-cell-2"><span class="fpd-icon-add"></span></div></div>
<div class="fpd-list-row" id="1609962838288"><div class="fpd-cell-0"><span class="fpd-current-color" style="background: #ffffff" data-colors=""></span></div><div class="fpd-cell-1"><textarea>Wanderlust</textarea></div><div class="fpd-cell-2"><span class="fpd-lock-element"><span class="fpd-icon-unlocked"></span></span></div></div>
I want to hide only the textareas and the parents element up to .fpd-list-row but keep the other div like .fpd-list-row .fpd-add-layer untouched. When I set the textarea to display none, the parent divs still exists. Is there a way hide the parent div up to ..fpd-list-row only when it contains <textarea>?
Loop through all divs, and use .find() to check for parent elements matching a certain selector.
$(document).ready(function(){
var divs = $("div");
for(var i = 0; i < divs.length; i++){
var current = divs[i];
if($(current).find("textarea").length != 0){
current.style.display='none';
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="fpd-cell-1"><textarea>My Text</textarea></div>
<div class="fpd-cell-2"><span class="fpd-lock-element">fpd-lock-element<span class="fpd-icon-unlocked">fpd-icon-unlocked</span></span></div>
<div class="fpd-cell-3"><textarea>My Text</textarea></div>
For the most concise solution (one liner), use:
$(document).ready(function(){
jQuery('textarea').parent().hide();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="fpd-cell-1"><textarea>My Text</textarea></div>
<div class="fpd-cell-2"><span class="fpd-lock-element">fpd-lock-element<span class="fpd-icon-unlocked">fpd-icon-unlocked</span></span></div>
<div class="fpd-cell-3"><textarea>My Text</textarea></div>
Check the children of the parent div:
divs = document.getElementsByTagName("DIV")
for (var i = 0; i < divs.length; i++) {
if (divs[i].childElementCount == 1 && divs[i].children[0].tagName.toLowerCase() == "textarea") {
divs[i].style.display = "none";
}
else { //for demonstration purposes
divs[i].style.backgroundColor="red"
}
}
<div class="fpd-cell-1"><textarea>My Text</textarea></div>
<div class="fpd-cell-2"><span class="fpd-lock-element">Outer Span<span class="fpd-icon-unlocked">Inner Span</span></span>
</div>
<div class="fpd-cell-3"><textarea>My Text</textarea></div>
Or, remove the parent of the textarea (idea credit of Spectric):
textareas = document.getElementsByTagName("TEXTAREA")
for (var i = 0; i < textareas.length; i++) {
textareas[i].parentNode.style.display = "none"
}
<div class="fpd-cell-1"><textarea>My Text</textarea></div>
<div class="fpd-cell-2"><span class="fpd-lock-element">Outer Span<span class="fpd-icon-unlocked">Inner Span</span></span>
</div>
<div class="fpd-cell-3"><textarea>My Text</textarea></div>
The first example hides the div only if there is one element in it, and it is the textarea, whereas the second method hides the parent of the textarea. Therefore, the first one can be used in situations where you need a textarea, and the second one just won't show any textareas regardless of the situation.
However, you could just make the dynamic content not generate the textarea and use a div:blank pseudo class in the css.
--------------- UPDATE ---------------
Update after code was updated in question.
textareas = document.getElementsByTagName("TEXTAREA")
for (var i = 0; i < textareas.length; i++) {
textareas[i].parentNode.parentNode.style.display = "none"
}
<div class="fpd-list-row fpd-add-layer" id="1609962837979">
<div class="fpd-cell-0"><span></span></div>
<div class="fpd-cell-1">Dein Foto</div>
<div class="fpd-cell-2"><span class="fpd-icon-add"></span></div>
</div>
<div class="fpd-list-row" id="1609962838288">
<div class="fpd-cell-0"><span class="fpd-current-color" style="background: white" data-colors=""></span></div>
<div class="fpd-cell-1"><textarea>Wanderlust</textarea></div>
<div class="fpd-cell-2"><span class="fpd-lock-element"><span class="fpd-icon-unlocked"></span></span>
</div>
</div>

Appendchild only works on last element

I am trying to create an element in JavaScript and apply it to all elements by class name. For this example I will use a paragraph for ease. The purpose of creating an element by JavaScript however is because I want to create a different element later on in my code.
In the code I am using, only the last element of the array of elements will contain the element created by the JavaScript. Could anyone explain why this is happening and what I could do to solve the problem accordingly to my requirement? I am trying to apply a whole element inside another element (so not just a value or property of a paragraph element).
My code:
//Creating my element:
let myElement = document.createElement("p");
/*let text = document.createTextNode("test");
myElement.appendChild(text);*/ //<-- Enable following to see text in result or check developer console for added paragraphs
//Single example:
let ele = document.getElementById("bar");
ele.appendChild(myElement);
//Not working...:
//Now class:
let eles = document.getElementsByClassName("foo");
for (i = 0; i < eles.length; i++) {
//eles[i].innerHTML = "abc";//<-- Does work (but hardcoded)?
//eles[i].innerHTML = myElement;//<-- returns "[object HTMLParagraphElement]"?
eles[i].appendChild(myElement); //<!-- Does work only for last element in array?
}
<div class="foo" id="bar">
</div>
<div class="foo">
</div>
<div class="foo">
</div>
<div class="foo">
<!-- Only this one will obtain the the paragraph element? -->
</div>
JSFiddle
You need to use cloneNode of element <p>, because appendChild moves it from its current position to the new position. See documentation
//Creating my element:
const myElement = document.createElement("p");
myElement.innerHTML = 'paragraph';
//Single example:
const ele = document.getElementById("bar");
ele.appendChild(myElement.cloneNode(true));
//Now class:
const eles = document.getElementsByClassName("foo");
for (let i = 0; i < eles.length; i++) {
eles[i].appendChild(myElement.cloneNode(true));
}
.foo {
border: 1px solid #333;
}
.foo p {
font-weight: bold;
}
<div class="foo" id="bar">
bar:
</div>
<div class="foo">
foo1
</div>
<div class="foo">
foo2
</div>
<div class="foo">
foo3
</div>
Your <p> element is appended only to the last because it is assigned to the myElementvariable. And because that variable is declared before your loop, each iteration will move the <p> tag through all your <div.foo>.
But if you declare your paragraph inside your for loop, a new one is created and appended to each of your blocks, because there are all different elements.
let eles = document.getElementsByClassName("foo");
for (i = 0; i < eles.length; i++) {
let myElement = document.createElement("p");
myElement.innerText = 'I am a paragraph.';
eles[i].appendChild(myElement);
}
<div class="foo" id="bar">
</div>
<div class="foo">
</div>
<div class="foo">
</div>
<div class="foo">
</div>

Javascript replace parent html (remove child)

I would like loop each iframes tags I've on my page and replace them all with a new div and on the way delete also the parent content so the new div will be the only child:
<div id="parent">
<p>this is parent content</p>
<iframe src="http://www.example.com" id="iframe10"></iframe>
</div>
Result should be:
<div id="parent">
<div id="newdiv">this is new div</div>
</div>
Here is my code for looping all iframes on page, the problem I don't understand how I can access each iframe parent and delete its contents:
var i, frames;
frames = document.getElementsByTagName("iframe");
for (i = 0; i < frames.length; ++i)
{
frame_id = frames[i].id;
}
Thanks
Shai
you can write it as following:
function remove_frames() {
var i = 0;
var frames = document.getElementsByTagName("iframe");
while (frames.length > 0) {
var f = frames[0];
var p = f.parentElement;
if (p) {
while (p.children.length > 0) {
p.children[0].remove();
}
p.innerHTML = "<div id=\"newdiv" + i + "\">this is new div" + i + "</div>";
i++; //increase id index
//p.appendChild(
}
}
}
<p><input type="button" value="Execute" onclick="remove_frames()" /></p>
<div id="parent">
<p>this is parent content</p>
<iframe src="http://www.example.com" id="iframe10"></iframe>
</div>
<p style="color:#6595ee">This element does not contain any sibling IFrame and must be exist in final result!</p>
<div id="parent2">
<p>this is parent2 content...</p>
<div>another element</div>
<iframe src="http://www.example.com" id="iframe20"></iframe>
<p>another element after iframe.</p>
</div>
var i, frames;
frames = document.getElementsByTagName("iframe");
for (i = frames.length; i; --i) // the array-like object shrinks every time a frame is removed so we have to loop backwards
{
//frame_id = frames[i].id;
// get the parent element
var parent = frames[i].parentElement;
// empty it (remove all it's elements)
while(parent.firstChild)
parent.removeChild(parent.firstChild);
// add the new div
var div = /* create new div */;
parent.appendChild(div);
}
You could use the parentElement
var iframes = document.getElementsByTagName("iframe");
iframes[0].parentElement.innerHTML = `<div id="newdiv">this is new div</div>`;
<div id="parent">
<p>this is parent content</p>
<iframe src="http://www.example.com" id="iframe10"></iframe>
</div>
You can use Node.parentNode to get the parent and Node.removeChild or Element.innerHTML to delete the content.
var i, frames;
frames = document.getElementsByTagName("iframe");
for (i = frames.length-1; i >=0; i--) {
var frame = frames[i];
if (frame.parentNode) {
var parent = frame.parentNode;
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
}
}
<div id="parent">
<p>this is parent content</p>
<iframe src="http://www.example.com" id="iframe10"></iframe>
</div>

use innerHTML to select class only if it is in a parent div

i currently have the code below which searches for the class and will replace the text.
how would i tweak it so it only will replace text if the parent tag is "#thumb-hockey-top"?
window.onload = function(){
//this captures all the elements with the spec classes
var soldItems = document.getElementsByClassName('product-mark sold-out');
//this changes each element 1 by 1 to new text
for(var i=0; i<soldItems.length; i++){
soldItems[i].innerHTML = "Coming Soon";
}
}
window.onload = function(){
//this captures all the elements with the spec classes
//just use a class
var soldItems = document.getElementsByClassName('sold-out');
//this changes each element 1 by 1 to new text
//var parentnode = document.getElementById('thumb-hockey-top')
for(var i=0; i<soldItems.length; i++){
if(soldItems[i].parentNode.id=='thumb-hockey-top'){
soldItems[i].innerHTML = "Coming Soon";
}
}
};
<div id="thumb-hockey-top">
<div class="product-mark sold-out"></div>
<div class="product-mark sold-out"></div>
<div class="product-mark sold-out"></div>
</div>
Use once
window.onload = function(){
//this captures all the elements with the spec classes
var soldItems = document.getElementById("thumb-hockey-top").getElementsByClassName('product-mark sold-out');
//this changes each element 1 by 1 to new text
for(var i=0; i<soldItems.length; i++){
soldItems[i].innerHTML = "Coming Soon"
}
}
<div id="thumb-hockey-top">
<div class="product-mark sold-out"></div>
</div>
<div class="product-mark sold-out"></div>
<div class="product-mark sold-out"></div>
Use multiple times
function myF(a, b){
// a = Id of parent element
// b = Class Name of element which you want to hack
var soldItems = document.getElementById(a).getElementsByClassName(b);
for(var i=0; i<soldItems.length; i++){
soldItems[i].innerHTML = "Coming Soon"
}
}
myF("thumb-hockey-top", "product-mark sold-out");
myF("thumb-hockey-bottom", "product-unmark sold-out");
<div class="example1">
<div id="thumb-hockey-top">
<div class="product-mark sold-out">EXAMPLE 1</div>
</div>
<div class="product-mark sold-out">EXAMPLE 1</div>
</div>
<div class="example2">
<div id="thumb-hockey-bottom">
<div class="product-unmark sold-out">EXAMPLE 2</div>
</div>
<div class="product-unmark sold-out">EXAMPLE 2</div>
</div>
You can get the parent element of an element using the parentElement attribute. Then just check its id.
var soldItem = soldItems[i];
if (soldItem.parentElement.id == "thumb-hockey-top") {
// do your thing
}

Issue using childNodes to process the childnodes javascript

I am trying to make display none for all the child nodes of a div. It works well with getElementsByTagname('*')
My Markup
<div id="container">
<div id="child1"></div>
<div id"child2">
<div id="inner-child"></div>
</div>
</div>
I would like to manipulate the display property of only the child1, child2.
function hideAllChildren(){
var elem = document.getElementById("container");
var children = elem.childNodes;
alert("children " + children.length)
for (i=0; i < children.length ;i++)
{
children[i].style.display="none";// Error - children[i].style undefined
}
}
Can you figureout what the issue could be ?
Not all the child nodes are elements, some are text nodes in some browsers and text nodes don't have a style property. Trying to access a property of a non-existant property throws an error.
Either test the node type or that the node has a (non-falsey value for its) style property first:
if (children[i].style) {
children[i].style.display="none";
}
However, you may find it much better to use a class and appropriate CSS rule and just add it to the parent element.
e.g.
<style type="text/css">
.hideAll * {
display: none;
}
</style>
</script type="text/javascript">
<button onclick="
document.getElementById('d0').className = 'hideAll';
">Hide all</button>
<button onclick="
document.getElementById('d0').className = '';
">Show all</button>
<div id="d0">Here is the div
<ul>
<li class="item">apple
<li class="item">orange
<li class="item">banana
</ul>
</div>
Why do you want to hide all the child nodes.
Where you can hide the parent and all the child will automatically hide.
So it will be simply:
function hideAllChildren(){
var elem = document.getElementById("container");
//alert("children " + children.length)
elem.style.display="none";
}
try this
<div id="container">container
<div id="child1">child1</div>
<div id"child2">Child 2
<div id="inner-child">inner-child</div>
</div>
</div>
<div id="clickme">Click me</div>
/// java script
$('#clickme').click(function() {
hideAllChildren();
});
function hideAllChildren(){
var elem = document.getElementById("container");
var children = elem.childNodes;
alert("children " + children.length)
$('#container').hide();
}

Categories

Resources