Using removeChild() properly - javascript

I have a function that appends a "div" child into a parent node, then I need to delete this child using the removeChild() method, but it doesn't work.
This is my code:
function ColorCards()
{for (i=0; i<numerocaselle; i++)
{document.getElementsByClassName("MemoryCards")[i].style.border="none"
var esempiocolore=document.createElement('div')
esempiocolore.style="position: relative; height: 80px; width: 130px; background-image: url('img/backcard"+cartaesempio+".png'); background-size: cover;"
document.getElementsByClassName("MemoryCards")[i].appendChild(esempiocolore)
}
}
function CleanColorCards()
{for (i=0; i<numerocaselle; i++)
{document.getElementsByClassName("MemoryCards")[i].style.border="dashed 3px #02A494"
document.getElementsByClassName("MemoryCards")[i].removeChild(document.getElementsByTagName("div"))
}
}
Does somebody have any suggestion on how to make it work?

You are passing an NodeList to removeChild, while you should pass a single node. Secondly, document.getElementsByTagName("div") is going to also find elements that are not children of the parent you are trying to remove a child from.
So do it like this:
// Avoid repetition of code, and store the result in a variable:
var nodelist = document.getElementsByClassName("MemoryCards");
for (var i=0; i<numerocaselle; i++){
const parent = nodelist[i];
parent.style.border="dashed 3px #02A494";
// Perform `getElementsByTagName` on the parent node only, and take first match:
parent.removeChild(parent.getElementsByTagName("div")[0]);
}
Note that querySelector is designed for getting one node-result, so that last line can read:
parent.removeChild(parent.querySelector("div"));

Just a couple notes:
Using a for loop is unnecessary. Having a variable to hold the count of the length of .MemoryCards will leave room for errors. Instead, I recommend an Array function such as .forEach() to iterate through your elements.
The bulk of your element styles should be handled with classes in CSS. By doing this your function will be more concise and easier to manage.
And, to answer your question:
To remove all child nodes for each .MemoryCards, I would recommend using a loop and the node.removeChild() method as it will perform faster than setting node.innerHTML=''.
See the comments in this post as why this method would be best.
let cartaesempio = 10;
ColorCards = () =>
Array.from(document.getElementsByClassName("MemoryCards"))
.forEach(e => {
e.classList.add('borderNone');
let esempiocolore = document.createElement('div');
esempiocolore.style.backgroundImage = `url('https://cdn.dribbble.com/users/285803/screenshots/1066705/dribbble.gif')`;
e.appendChild(esempiocolore);
});
CleanColorCards = () =>
Array.from(document.getElementsByClassName("MemoryCards"))
.forEach(e => {
e.classList.add('boderDashed');
while (e.firstChild) {
e.removeChild(e.firstChild);
}
});
ColorCards();
CleanColorCards();
/* Children of the .MemoryCards nodes */
.MemoryCards div {
position: relative;
height: 80px;
width: 130px;
background-size: cover;
}
.borderNone {
border: none;
}
.boderDashed {
border: dashed 3px #02A494;
}
<div class='MemoryCards'></div>
Hope this helps,

getElementsByTagName returns node list(array). You will have to select a node. Maybe something like this would be useful for you:
document.getElementsByTagName("div")[0]
https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName

just check your error ! document.getElementsByTagName("div") return an array of div that's basic js meaning you've to search more by ourself.
Use a manual, like the one at w3schools or a book whatever :-)

You are passing removeChild all the divs in the document.
Try replacing this line:
document.getElementsByClassName("MemoryCards")[i].removeChild(document.getElementsByTagName("div"))
With:
var memoryCardsEl = document.getElementsByClassName("MemoryCards")[i];
memoryCardsEl.removeChild(memoryCardsEl.getElementsByTagName("div")[0]);

Related

wait until insertAdjacentHTML is done then continue code

I dynamically add content to my web page using insertAdjacentHTML like the following example code:
...
for(let i = 0; i < someArray.length; i++)
{
someDiv.insertAdjacentHTML('beforeend', `<div id='post-${i}'>...</div>`);
let insertedObject = document.getElementById(`post-${i}`);
// at this point `insertedObject` is null!
}
...
but content does not get added instantly and insertedObject is null, now I have tried finding fixes and the most common is to put a setTimeout to wait until the new element is added but thats too slow! could there be a better way to get the newly added element?
Following on from mine and Peter's comments here's how I might approach this.
Create a containing element. Let's assume that you want it to catch click events from the HTML you're about to insert (event delegation). Add a listener to it that, for example, calls a function that logs the id of the clicked post.
Then map over the array to produces an array of HTML strings that you then join up, and then use insertAdjacentHTML to add that HTML to the container.
// Create the container and add a listener to it
const container = document.querySelector('.container');
container.addEventListener('click', handleClick);
const arr = [1, 2, 3, 4];
// `map` over the array to create some HTML strings, joining
// them up when the iteration is complete
const html = arr.map(n => {
return `<div class="post" id="post-${n}">${n}</div>`;
}).join('');
// Add the HTML to the container
container.insertAdjacentHTML('beforeend', html);
// When the container catches an event fired from
// a child element, check that it's an element with
// a post class, and then log its id
function handleClick(e) {
if (e.target.matches('.post')) {
console.log(e.target.id);
}
}
.container { width: 50%; }
.post { background-color: lightgray; margin: 0.25em; padding: 0.25em; }
.post:hover { background-color: yellow; cursor: pointer; }
<div class="container">
</div>

Wrap innerHTML in div

I have a JS function that updates descriptions of products when variables are changed. Here is where the element is updated.
Product.Config.prototype.updateProductShortDescription = function(productId) {
var shortDescription = this.config.shortDescription;
if (productId && this.config.childProducts[productId].shortDescription) {
shortDescription = this.config.childProducts[productId].shortDescription;
}
$$('#product_addtocart_form div.short-description').each(function(el) {
el.innerHTML = shortDescription;
});
};
Works great but would like to wrap the output in a div. Does anyone know a way or wrapping innerHTML in a tag before when updating it?
Thank you.
Don't use innerHTML if you're just setting text. It is incredibly insecure and will happily execute scripts and contact any server that the text you're using can instruct the browser to do by simply including some malicious HTML code.
Use the .textContent property instead.
But better yet, since it looks like you're using jQuery anyway, just use jQuery's built-in way to construct elements as needed:
let div = $(`<div></div>`).text(description);
$(`.my-element`).append(div);
And if you already have elements:
let update = $(`<div></div>`).text(description);
$(`#your.query-selector goes:here()`).empty().append(update);
(because jQuery lets you chain calls in a way that make them apply to every element in a selection, this will set that div-wrapped description as content for every element in the query result)
I don't know what your $$ function is from, but there is my proposal in vanilla JS:
const descriptionText = "my short description";
const descriptionElements = document.querySelectorAll('#product_addtocart_form div.short-description');
Array.from(descriptionElements).forEach(function(el) {
const newDiv = document.createElement('div');
newDiv.classList.add('customDiv');
newDiv.textContent = descriptionText;
el.appendChild(newDiv);
});
div {
border: 1px dotted silver;
padding: .5em;
margin: .25em;
}
<div id="product_addtocart_form">
<div class="short-description">1</div>
<div class="short-description">2</div>
<div class="short-description">3</div>
</div>
Edit:
After reading your first comment, in your code you just have to replace:
el.innerHTML = shortDescription;
by:
const descWrapper = document.createElement('div');
descWrapper.innerHTML = shortDescription;
el.appendChild(descWrapper);

How can I access all elements within a NodeList?

With pure JavaScript, I've used this line to return a NodeList of multiple elements:
var elements = document.getElementsByClassName('icon-wrapper');
I can then use any of these lines to target a specific index from that NodeList:
elements[0];
elements[1];
elements[2];
But when I try to include multiple objects from the NodeList or loop through them, only one node, the last one requested, is returned.
I'm trying to apply .appendChild to all nodes with the class of icon-wrapper
Here is the code:
var square = document.createElement('div');
square.style.width = "32px";
square.style.height = "32px";
square.style.zIndex = "-1";
square.style.position = "absolute";
square.style.backgroundColor = "red";
var elements = document.getElementsByClassName('icon-wrapper');
var requiredElement = elements[0, 1, 2];
requiredElement.appendChild(square);
body {
background: lightyellow;
}
.icon-wrapper {
width: 30px;
height: 30px;
background: green;
float: left;
margin: 2px;
}
<div class="icon-wrapper">
<svg></svg>
</div>
<div class="icon-wrapper">
<svg></svg>
</div>
<div class="icon-wrapper">
<svg></svg>
</div>
Are you trying to achieve something like this? Check out :
https://jsfiddle.net/cd2gbuc9/3/
i.e:
1) Get the required elements.
For each of them in a loop:
1) Create a square
2) Append the square to the element
There can be very different ways to achieve this. Easy ways create 3 different variable for square to be added. and then append them each using a for loop to each element.
when you use getElementsByClassName result is "NodeList". the NodeList is a array of "node" and node can be "element" or "attribute" or "comment" and or "text".
you most check noteType in loop and only add elemnt to node type element that nodeType is 1.
for(var i = 0; i < elements.length; i++)
{
if(elements[i].nodeType === 1){
elements[i].appendChild(square);
}
}
for more information see this reference:
http://www.w3schools.com/jsref/prop_node_nodetype.asp
var requiredElement = elements[0,1,2];
This won't do what you think it does, whatever that is. It is equivalent to var requiredElement = elements[2];.
You need to get out of your jQuery mindset. You could start by not referring to "pure JavaScript"; all JavaScript is pure. DOM elements are not jQuery-like magical groups of things that you can simultaneously apply classes to, or append all of to something or append something to all of, or whatever. You will have to LOOP over a list resulting from something like getElementsByClassName and do whatever you want element-by-element.
In general, the use of getElementsByClassName itself is a code smell. Do not use classes as way to identify elements that you will then manipulate and insert and delete. Classes are a CSS construct, whose main purpose in life is to associate elements with CSS rules whose selectors specify those classes.
For example, instead of creating a div in code with laboriously assigned individual styles, and then trying to insert it here and there with code (hint: you can only include it in one place; if you insert it somewhere else it will MOVE there and be removed from where it was before), just add it to your HTML, with a class which brings in all those styles.
Your logic is in error in two ways:
The statement elements[0,1,2] is equivalent to elements[2]; it's only returning the last element, not all of them.
You're trying to add the same ONE square to multiple elements. Even without error number 1, it would end up on the last element anyway, because there is only one square to go around. If you want one square on every element in the NodeList, you need to create one square for every element in the NodeList, and then loop through it, appending a square on each iteration. For example:
Convert the NodeList to an Array:
var node_list_array = Array.prototype.slice.call(div_list);
and append a different square to each element:
node_list_array.forEach(function(elem, index){
elem.appendChild (squares[index]);
});
First, Document.getElementsByClassName. This is something to be aware of if you are removing/adding elements during a loop, as it is a live HTMLCollection.
var requiredElement = elements[0,1,2]; is not a way to access indexed items from an array like object, I don't know where you got that idea.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
So you need to iterate array like objects, by using iteration methods.
You need to create a new red box for each appendChild, otherwise you will just move the box from one place to another.
An example written in ES6.
function createARedSquare() {
const square = document.createElement('div');
// use CSS for a class when possibe
square.className = 'redSquare';
return square;
}
//const slice = Function.prototype.call.bind(Array.prototype.slice);
// Gets a 'live' HTMLCollection and converts to an Array, a fixed node content
function getElementsByClassName(className) {
// You could use Array#slice here instead of Array.from if < ES6
return Array.from(document.getElementsByClassName(className));
}
function addRedSquareToEachIconWrapper() {
getElementsByClassName('icon-wrapper')
.forEach(iconWrapper => iconWrapper.appendChild(createARedSquare()));
}
addRedSquareToEachIconWrapper();
body {
background: lightyellow;
}
.icon-wrapper {
width: 30px;
height: 30px;
background: green;
float: left;
margin: 2px;
}
.redSquare {
width: 32px;
height: 32px;
background: red;
z-index: -1;
position: absolute;
}
<div class="icon-wrapper">
<svg></svg>
</div>
<div class="icon-wrapper">
<svg></svg>
</div>
<div class="icon-wrapper">
<svg></svg>
</div>

How do I repeat div classes using JavaScript only?

Okay, I'm unsure how to word the question, but basically I want to repeat my div containers that have a class of "blocks" using only javascript, no HTML (other than the HTML needed to start a page). IF I were doing this using HTML the result should look exactly like this.
http://jsfiddle.net/nqZjB/1/
<div class = "blocks"> <!-- Repeats three times -->
However as I stated in the description I do not want to use any HTML, so here is my fiddle with javascript only.
How do I make div class blocks repeat three times as in my HTML example using only javascript? Of course in real life I would use HTML for this as javascript is unnecessary, but I want to do this in pure javascript so I can learn. Also as a sidenote if you have a better way as to how I should have worded the question, let me know.
Thanks (:
http://jsfiddle.net/TbCYH/1/
It's good you see the use of making a function of a re-occurring pattern.
Before posting it in StackOverflow, have you tried doing it yourself?
jsfiddle: http://jsfiddle.net/kychan/W7Jxu/
// we will use a container to place our blocks.
// fetch the element by id and store it in a variable.
var container = document.getElementById('container');
function block(mClass, html) {
//extra html you want to store.
return '<div class="' + mClass + '">' + html + '</div>';
}
// code that loops and makes the blocks.
// first part: creates var i
// second: condition, if 'i' is still smaller than three, then loop.
// third part: increment i by 1;
for (var i = 0; i < 3; i++) {
// append the result of function 'block()' to the innerHTML
// of the container.
container.innerHTML += block('block', 'data');
}
Edit: JS has changed a lot since the original post. If you do not require compatibility, use const, template literals, class and querySelector to make the code a bit cleaner. The following code has a Builder class and assumes there is a div with ID 'container':
// create class builder.
class Builder {
// create constructor, accept an element selector, i.e #container.
constructor(targetContainerSelector) {
// search element by given selector and store it as a property.
this.targetContainer = document.querySelector(targetContainerSelector);
}
// method to append to innerHtml of target container.
appendUsingInnerHtml(divAsHtml) {
this.targetContainer.innerHTML += divAsHtml;
}
// method to append to target container using DOM elements.
appendUsingDom(divAsDom) {
this.targetContainer.appendChild(divAsDom);
}
}
// constant to hold element selector.
const myTargetContainer = '#container';
// constant to set the class if required.
const myDivClass = 'my-class';
// constant to hold the instantiated Builder object.
const builder = new Builder(myTargetContainer);
// loop 3 times.
for (let i=0; i<3; i++) {
// call method to append to target container using innerHtml.
builder.appendUsingInnerHtml(`<div class="${myDivClass}}">innerhtml div text</div>`);
// OR.. build using DOM objects.
// create the div element.
const div = document.createElement('div');
// create text element, add some text to it and append it to created div.
div.appendChild(document.createTextNode('dom div text'));
// call method to append div DOM object to target container.
builder.appendUsingDom(div);
}
Please note: Every time something is added to the DOM, it forces the browser to reflow the DOM (computation of element's position and geometry).
Adding everything at once, improve speed, efficiency and performance of a code.
(ref: document.createDocumentFragment)
window.onload = Create();
function Create() {
// create the container
var mainContainer = document.createElement('div');
mainContainer.id = 'mainContainer';
// add all style in one go
mainContainer.setAttribute('style', 'witdht: 400px; height: 200px; border: 2px solid green; margin-left: 20px;');
var divBlocks1 = document.createElement('div');
divBlocks1.className = 'blocks';
divBlocks1.setAttribute('style', 'width: 100px; heigth: 100px; border: 1px solid black; margin-left: 20px; margin-top: 10px; floar: left;');
var divBlocks2 = divBlocks1.cloneNode(false); // copy/clone above div
var divBlocks3 = divBlocks1.cloneNode(false); // copy/clone above div
// everything is still in memory
mainContainer.appendChild(divBlocks1);
mainContainer.appendChild(divBlocks2);
mainContainer.appendChild(divBlocks3);
// now we append everything to the document
document.body.appendChild(mainContainer);
}
Good luck
:)
for(var d=0;d<10;d++){
var aDiv = document.createElement('div');
aDiv.className = "block";
document.getElementsByTagName('body')[0].appendChild(aDiv);
}
Rather than creating the elements before hand and then appending them to the main container, consider dynamically creating and appending them in a loop.
http://jsfiddle.net/TbCYH/6/
for(var i = 0; i < 3; i++) {
var divBlock = document.createElement("div");
divBlock.className = "blocks";
mainContainer.appendChild(divBlock);
}
In the above code snippet a div is being created and appended for each iteration of the loop (which is set to cease at 3).
Also if possible, always use CSS classes rather than modifying the styles for each div directly.

Javascript Array not working correctly with $.makeArray

I'm trying to make an array out of a set of div's on a page using jQuery. I basically want to randomise the way they are displayed so for the moment, I'm just trying to cycle through them. Everything appears to work, except that I only ever see the last array item, even though it performs the action the same number of times as there are elements in the array, and adds the correct behaviour.
The JS is:
<script>
$(document).ready(function(){
var obj = $('.item');
var arr = $.makeArray(obj);
$('.array').html('');
$.each(arr, function(k,v){
$('.array').html(v).fadeIn(250).delay(2000).fadeOut(250);
});
});
</script>
And the markup is:
<div class="array">
<div class="item">First</div>
<div class="item">Second</div>
<div class="item">Third</div>
<div class="item">Fourth</div>
</div>
I'm not sure that it's relevant, but here's the CSS, just in case:
div.item {
display: inline; float: left; width: 960px; height: 260px; font-family: helvetica; font-size: 10px; text-align: center; background: #eee; border: solid 1px #888;
}
All I get is the div with the text "Fourth" fading in and out 4 times. This tells me it's iterating through the array fine (as it's using the count) but why am I only seeing the last element? Anyone any thoughts?
Thanks,
T
The loop is overwriting the content of the array div in every iteration of the loop. thus you only see the result of the last iteration.
When you use .html(...) it is the same as .empty().append(...). So what you loop does is empty the content 4 times in a row, and only the append after the last empty will actually take effect.
If you wish the elements to fadein/fadeout one after another you can do it like this:
$(document).ready(function(){
var obj = $('.item');
$('.array').html('');
obj.each(function(i){
$('.array').append($(this).hide().delay(i * 2500).fadeIn(250).delay(2000).fadeOut(250));
});
});
you can see it running here: http://jsfiddle.net/9Xg8X/
Also worth mentioning is that the calls to the effect methods don't block. That is why the loop is already finished before the first effect occurs.
You don't say what you actually want, but if you want to have every element appended and appear/disappear one after another, you can do something like this:
$(document).ready(function(){
var obj = $('.item').detach();
var count = obj.length;
var target = $('.array');
var display = function(i) {
var element = $(obj[i]);
var cb = (i < count-1) ? function(){display(i+1)} : function(){};
element.appendTo(target).hide().fadeIn(250).delay(2000).fadeOut(250, cb);
};
display(0);
});
There is actually no need to use $.makeArray().
DEMO

Categories

Resources