How can I access all elements within a NodeList? - javascript

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>

Related

Adding/changing properties in all the tags of a type (odd results)

I am looking for a way to add/change some properties in all tags of a certain type after the page is loaded.
I found two ways. But for some reason that I don't understand, the results are different.
Method 1 - adding a style tag as the last child of <html>.
var style = document.createElement( "style" );
style.innerHTML = `
div {
background-color: black;
color: white;
}
`;
document.documentElement.appendChild( style );
Result of style tag as last HTML child method
Method 2 - Getting all the tags of that type and painfully having it change them one.
var elems = document.body.getElementsByTagName("div");
for( let i = 0; i < elems.length; i++ ){
elems[i].style.backgroundColor = "black";
elems[i].style.color = "white";
}
Result of loop/in-line method
So I was wondering why the results between the two methods are different, and I'm also happy to learn about other methods of changing all tags of a type of course.
the first method it creates a <style> tag and sets its attributes like you would write the css in the <style> tag or enter it via <link>
the 2nd method uses the style attribute of each tag which is also W3C standard but scoped to just that tag and has syntactic limitations (cannot use selectors like :hover , :focus...)
the 2nd method is faster than 1 and has higher precedence and no error about scoped it is preferred in javascript
Here, Method 1 adding a new style tag to the document and applying CSS styles using the innerHTML. All tags on the page that match with the selector div will have the styles using this technique. However, depending on how much styling is required, this technique may result in a slower page load.
Then,another method which is selecting the background and color properties of each div element on the page by looping through all of them. This approach is quicker because it doesn't need to wait for new styles to load.
So, another method you can use just by changing a bit in your second method is:
var elems = document.querySelectorAll("div");
for( let i = 0; i < elems.length; i++ ){
elems[i].style.backgroundColor = "black";
elems[i].style.color = "white";
}
Nevertheless, you can also use Jquery for shortcut execution :
$("div").css({
"background-color": "black",
"color": "white"
});
The difference was priority. The same inline results of method 2 (loop/inline), were achieved with method 1 (<style> as last child) by adding the !important rule due to the increased priority. Like so:
div {
background-color: black !important;
color: white !important;
}
Simple example demoing how to easily and fully revert all changes, and be left back with the original HTML:
<!DOCTYPE html>
<html>
<head>
<style>
p {
background-color: red;
}
</style>
</head>
<body>
<p>Text sample.</p>
<p style="background-color: grey">Text sample.</p>
<button onclick="test1()">Remove temporary changes</button>
</body>
<script>
function test1() {
document.getElementById("tempChanges").remove();
}
</script>
<style id="tempChanges">
p {
background-color: green !important;
}
</style>
</html>

Using removeChild() properly

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]);

My javascript hint script does not work. Accessing and manipulating the DOM

Knowledge level: Beginner
What I expect from my code:
The user clicks on a class named open.
The textNode within 'open' gets replaced with a - sign.
Then I go to the first child of the parent of that class which is an h2 tag and get the title in order to place it within the sibling of 'open' named 'info'.
At last info turns visible.
The ternary operator is to check if we have only a nodeType of 3 within the firstChild. If yes get the text, if not then get the entire innerHTML.
Since I get a html collection from getElementsByClassName I tend to create a loop so that I can modify the style.
Why I do this or don't use jQuery:
I am trying to push myself and learn how to effectively manipulate the dom without third party libraries. I would appreciate hints on improving my code but please keep the basic structure the same as I am still not into advanced short cuts and I am trying to learn not copy.
Problem I am not sure how "correct" my idea of manipulating the dom is. I could not get this to work, neither do I know how to effectively tell javascript to handle only the currently clicked element.
http://jsfiddle.net/r7bL6vLy/28/
function wrapper () {
var open = document.getElementsByClassName(open);
function trigger (){
var info = this.nextSibling;
var getTitle = this.parentNode.firstChild.(nodeType == 3 ? textContent : innerHTML)
this.removeChild(textContent);
this.appendChild(document.createTextNode('-'));
info.appendChild(document.createTextNode(getTitle + 'details'));
info.style.visibility = 'visible';
}
for (i = 0; i < open.length; i++) {
open[i].addEventListener('click', trigger, false);
}
}
HTML
<div id='A'>
<h1>Stackoverflow Question</h1>
<div class='open'>+</div>
<div class='info'>Content A...</div>
</div>
<div id='B'>
<h1>Stackoverflow Question</h1>
<div class='open'>+</div>
<div class='info'>Content B...</div>
</div>
this.nextSibling will give you the textNode representing the whitespace between the elements. Use .this.nextElementSibling instead.
You don't need to do any traversal to change the + to a - since you already have the open element. Just assign it the new value.
this.textContent = "-";
To assign the h2 content, simple use .previousElementSibling.textContent and assign it to info.textContent
info.textContent = this.previousElementSibling.textContent
Some things you were doing wrong were:
using invalid syntax here:
var getTitle = this.parentNode.firstChild.(nodeType == 3 ? textContent : innerHTML)
Should have been an if statement, though the condition doesn't really seem necessary. You can use .textContent on an element too, as long as you don't need the HTML representation.
Technically you could do this:
var child = this.parentNode.firstElementChild;
var getTitle = child[child.nodeType === 3 ? "textContent" : "innerHTML"];
...but that's pretty ugly. Avoid clever tricks like this.
Using textContent as a reference to an element:
this.removeChild(textContent);
Things that could be improved:
When changing text, favor manipulating .textContent over creating new text nodes. The existing nodes are mutable and so can be reused.
If you want to copy a section of the DOM to a new location, don't use .innerHTML but instead use .cloneNode(true).
var copy = myElem.cloneNode(true);
targetElem.appendChild(copy);
Otherwise you're taking the DOM nodes, serializing them to HTML and then immediately parsing the HTML into new nodes. All that string manipulation can be avoided simply by cloning.
You almost had it:
function trigger (){
var info = this.nextElementSibling,
getTitle = this.parentNode.firstElementChild.textContent;
this.textContent = '-';
info.appendChild(document.createTextNode(getTitle + 'details'));
info.style.visibility = 'visible';
}
var open = document.getElementsByClassName('open');
for (i = 0; i < open.length; i++)
open[i].addEventListener('click', trigger, false);
function trigger (){
var info = this.nextElementSibling,
getTitle = this.parentNode.firstElementChild.textContent;
this.textContent = '-';
info.appendChild(document.createTextNode(getTitle + 'details'));
info.style.visibility = 'visible';
}
var open = document.getElementsByClassName('open');
for (i = 0; i < open.length; i++)
open[i].addEventListener('click', trigger, false);
#a, #b {
width:50%;
height:100%;
margin:auto;
}
h1 {
width:100%;
font-size:160%;
text-align:center;
}
.open {
width:22%;
margin:auto;
padding:10% 0;
line-height:0;
font-size:150%;
text-align:center;
font-weight:bold;
background:yellow;
border-radius:100%;
}
.info {
width:100%;
padding:5%;
margin:5% auto 0 auto;
text-align:center;
background:ghostwhite;
visibility:hidden;
}
<div id='A'>
<h1>Stackoverflow Question</h1>
<div class='open'>+</div>
<div class='info'>Content A...</div>
</div>
<div id='B'>
<h1>Stackoverflow Question</h1>
<div class='open'>+</div>
<div class='info'>Content B...</div>
</div>
Remember that whitespace between elements becomes a text node. So better use firstElementChild and nextElementSibling instead of firstChild and nextSibling.

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