Javascript updates HTML classes before it is expected to - javascript

The problem
I am making Pacman, and I wrote the following function to initialize the board. How is it possible that the board is rendered, even though I only add the styling classes to the square elements in my 'squares' array? As far as I can see I never updated the classes of the div elements inside my 'grid' (which holds the actual div html elements that are shown to the user).
The function
function createBoard() {
for (let i = 0; i < layout.length; i++) {
const square = document.createElement('div')
grid.appendChild(square)
squares.push(square)
if (layout[i] === 0) {
squares[i].classList.add('pac-dot')
} else if (layout[i] === 1) {
squares[i].classList.add('wall')
} else if (layout[i] === 3) {
squares[i].classList.add('power-pellet')
}
}
}
The function iterates through an array (layout) that holds information of how the board should look. For each element of that array a div (square) is created which is then added to the 'grid' (a div element in my html file that holds all newly created divs). The square div is then added to the 'squares' array, which also holds all the square divs, but is not present in my html file.
The second part of the function adds a class to the square in the squares array , based on how the board should look. The result is the following board:

Assuming grid is a DOM element, then grid.appendChild(square) will add the square to the DOM. After that, any change you apply to the element's style will be visible.
So for example, let's say you have the following HTML:
<div id="grid"></div>
Now let's add a child div to it:
const grid = document.getElementById("grid");
const square = document.createElement("div");
grid.appendChild(square);
This actually changes the HTML, to be as if it was written like this:
<div id="grid">
<div></div>
</div>
Adding a class to the div will also change the markup. So after this:
square.classList.add('wall');
The HTML will look like so:
<div id="grid">
<div class="wall"></div>
</div>

Related

How to insert an adjacent element if child element contains specific text?

I'm inserting an element with the following code:
var descriptions = document.querySelectorAll(".product-item-info");
function showDescription() {
descriptions.forEach(description => {
description.insertAdjacentHTML("beforeend", "<div class='description'>Some text</div>");
});
}
showDescription();
This works well. But how do I check if a child of .product-item-info contains specific text, and if so then not add the markup to said element?
I know how to do this with jQuery.
Edit: change afterend to beforeend
You do it with the DOM the same way you do it with jQuery, it's just you have to do a bit that jQuery does behind the scenes in your own code: Seeing if elements contain that text. There's no real shortcut, you just have to look at the text of the element:
const descriptions = document.querySelectorAll(".product-item-info");
function showDescription() {
for (const description of descriptions) {
if (!description.textContent.includes("Some text")) {
description.insertAdjacentHTML("beforeend", "<div class='description'>Some text</div>");
}
}
}
showDescription();
(The optional chaining handles the case where a description doesn't have a next element sibling.)

Problem with access to dynamically created elements

I've asked a similar question before but that was about events related to dynamically created elements. Now I just want to change class lists of dynamically created img tags in a div. Here is what is all about, I want to create a slider that contains three images taken with src and alt attributes taken from a JSON file. Function created all three normally and as I wanted put displayNone class on every img except first. Another function should on every 3 seconds check in a loop all the dynamically created img tags for the one that does not contain displayNone class and to add to that particular img class displayNone and then to get next, or if it is last first, img and to remove his displayNone class. I created var inside function to get an array of these images. Does anybody know what the solution is?
function showSlides(slidesJsonData){ // there is no problem with this function it generates img tags when window is loaded
var writingSlides = "";
for (slide in slidesJsonData){
if(slide==0){
writingSlides += `<img src="${slidesJsonData[slide].src}" alt="${slidesJsonData[slide].alt}"/>`;
}
writingSlides += `<img class="displayNone" src="${slidesJsonData[slide].src}" alt="${slidesJsonData[slide].alt}"/>`;
}
document.querySelector("#slider").innerHTML=writingSlides;
}
function slideShow(){
var images = document.querySelectorAll("#slider img");
images.forEach((img,index) => {
if(index<images.length-1){
if(!img.classList.contains("displayNone")){
img.classList.add("displayNone");
img[index+1].classList.remove("displayNone");
}
}
else {
img.classList.add("displayNone");
document.querySelector("#slider img:first-of-type").classList.remove("displayNone");
}
});
setTimeout("slideShow()",3000);
}
slideShow();
Now error is:
Uncaught TypeError: Cannot read property 'classList' of undefined
at main.js:73
at NodeList.forEach (<anonymous>)
at slideShow (main.js:69)
at <anonymous>:1:1
try updating your sliderImages variable before accessing it because it probably is being created before content as finished loading
You are using a for in loop that exposes other enumerable properties of the element collection unrelated to element indexing. Element collections are not proper arrays but rather are array-like objects
When you try to pass these properties to sliderImages[sliderImage] inside your loop they do not return an element with a classList property and no sub property contains which throws the error.
Use a for loop based on length or a for of loop or sliderImages.forEach(callback)
Simple property output example of the for in
const li = document.querySelectorAll('li')
for (let x in li) {
if (isNaN(x)) {
console.log(`Property "${x}" is not an element index`)
} else {
console.log(`Key "${x}" is an element index`)
}
}
<ul>
<li></li>
<li></li>
</ul>
Try this
var images = document.querySelectorAll("#slider img");
let cnt= 0
function slideShow(){
images[cnt].classList.toggle("displayNone")
cnt++
if (cnt>= images.length) cnt = 0;
images[cnt].classList.toggle("displayNone")
}
setInterval(slideShow,3000);

javascript list with content toggling acting like accordion,

I have a problem with pure JS list. I want the div with content (below each li) to display once the particular li is clicked. No idea how to make it work. I'm either ablo to show one "content" block or all of the at once (by adding the once that are commented now. Any help appreciated.
const channelList = document.getElementById("station-list");
channelList.addEventListener("click", function (e) {
const target = e.target;
if (target.matches("li")) {
content.classList.toggle("show");
//content2.classList.toggle("show");
//content3.classList.toggle("show");
//content4.classList.toggle("show");
//content5.classList.toggle("show");
//content6.classList.toggle("show");
}
})
https://jsbin.com/valojoruhe/1/edit?html,js,output
Try something like this:
Fiddle example: https://jsfiddle.net/eugensunic/9ebxvt1u/
your div's must be inside a li, otherwise you'll have invalid html
do not use multiple id's content, content1, content2 ... it's useless, rather define a class.
after you've clicked on the li element find the div element (filter it out) and then apply the desired styling to it.
you'll need to adjust the layout for your div to be below the paragraph, for now it's on the right side but toggles appropriately.
JS
const getAllLiElements = document.querySelectorAll('li');
for (let i = 0; i < getAllLiElements.length; i++) {
let element = getAllLiElements[i];
element.addEventListener('click', () => {
const dummyText = Array.from(element.children).filter(htmlNode => htmlNode.tagName === 'DIV')[0]
dummyText.classList.toggle('show')
})
}

Change color of all elements in class on click JavaScript

I have an image (SVG) of a human body. I would like to use JavaScript so that when I click a particular area (say, the lower leg) then all of the elements with the class "lower-leg" (even if not clicked) have their color changed -- this makes it much easier for the user.
Here is the JavaScript I currently have:
function changeclassstyle() {
var c = document.getElementsByClassName("lower-leg");
for (var i=0; i<c.length; i++) {
c[i].style.fill = "red";
}
}
The problem with this code is that it is only generalized for "lower-leg". I may have over a dozen classes I would like this to work for and don't think it is efficient to write 12 functions with the only change being the class name. Is there a way to grab what class was selected and then input that in the function?
--
Additionally, I would love to figure out how, once that section of the body is selected, I can store the class name. I would, in the end, want to store the selection, along with other inputted information in a database. But, this may be for a future question unless someone can help!
Here's how I would do it (tested on a couple of div's).
What we're doing is passing the event object to the event handler (your changeclassstyle() function). It then uses the class of the clicked-on item (the event target's class) and changes everything else on that page with that same class name to use your new desired CSS style.
function changeclassstyle(e) {
// Get all items that have the same class as the item that was clicked
var limbs = document.getElementsByClassName(e.target.className); // for div's and the like
// var limbs = document.getElementsByClassName(e.target.className.baseVal); // turns out this is needed for SVG items
// "limbs" is an HTMLCollection, not an array, so functions like .foreach won't work; C-style for-loops or modern for/let/of loops are better
for (let item of limbs) {
item.style.backgroundColor = 'red';
// item.style.fill = 'red'; // This is probably what you need for your SVG items
}
// You could still use your C-style for loop if needed/wanted
/*
for (var i=0; i<limbs.length; i++) {
limbs[i].style.fill = "red";
}
*/
}
The onchange call looks like this (using my div as the example):
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
The whole example with simple div's.
<html>
<head><title>stuff</title></head>
<body>
<script type="text/javascript">
function changeclassstyle(e) {
// For debugging. You may want to expand 'e' here in your browser's debug tools if you're not seeing the values you need/want
console.log(e)
var limbs = document.getElementsByClassName(e.target.className.baseVal);
for (let item of limbs) {
item.style.backgroundColor = 'red';
}
}
</script>
<style type="text/css">
div {
height: 100px;
width: 100px;
background-color: 'white';
border: 1px solid black;
}
</style>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
</body>
</html>
You can use parameters in function where you pass class and color like below
function changeStyle(cls,clr) {
let elems = document.getElementsByClassName(cls);
if(!elems) return;
for (let elem of elems) {
elem.style.color = clr;
}
}
As per the iteration of many classes like i said you can store classes in array and iterate each of them.
let classes = ['one','two','three','four'];
classes.forEach(function (cls) {
changeStyle(cls,"red");
});
You can play with fiddle here if you want to test/experiment: https://jsfiddle.net/thrL5uqw/8/
Note: Change style property as you wish, For now i have used color for demo
I'm a bit late to the party, but here's my take on the problem.
Like the others told you, you'll need to use an additional parameter to your function to specify the class you want to modify your elements (or try to figure out the class from the clicked element), therefore you should have something like that:
/**
* This function will handle the click event on one of the part of the SVG.
* #param {string} lClass This the class of the element to modify
*/
function handleClick(lClass) {
for (let e of document.getElementsByClassName(lClass)) {
// Here you can do all the changes you need on the SVG element.
e.style.fill = "red";
}
}
And when it comes to the event binding, you could do like the other suggested and add the onclick event binding propery on the HTML Element, or you could bind it in you JS with the addEventListener function (that way you don't have to repeat the onclick property on each of your SVG elements).
// For each element of all the listed class, bind the "click" event to the handleClick function
const listenClass = [/*List of your classes*/];
for (let l of listenClass) {
for (let e of document.getElementsByClassName(l)) {
e.addEventListener('click', handleClick.bind(this, l));
}
}
Demo: https://plnkr.co/edit/gay2yBaVi5QD868fsTa6?p=preview
I hope it helped.

Toggle class with onclick and checkboxes

I'm making an inventory page for a website. I want to be able to sort the items with check boxes. What i think i need is to apply a div class to each of them and use document.getElementsByClassName to change the display to none for the ones i want hidden.
Anyway i tried with document.getElementsById, but i could only change the first one with the corresponding Id. Apperarently i need to change the class. This is where im at now.
P.S The [sc:1 ] is shortcoder for wordpress. It seemed to work when i used Id.
<script>
function toggle() {
var e = document.getElementsByClassName('truck');
if(e.style.display == "block")
e.style.display = "none";
else
e.style.display = 'block';
}
</script>
<input type="checkbox" id="checkme" onclick="toggle()">Dump Truck
<div class="truck">This is text that should hide</div>
<div class="truck">[sc:1 ]</div>
<div class="sweeper">[sc:2 ]</div>
<div class="sweeper">[sc:3 ]</div>
You have to iterate over the set that document.getElementsByClassName('truck'); returns:
for (var i = 0; i < e.length; i++) {
if(e[i].style.display == "block")
e[i].style.display = "none";
else
e[i].style.display = 'block';
}
document.getElementsByClassName returns a set of elements that match that class name, so you have an array of DOM elements: [elem1, elem2, elem3, ..]. You can't just simply set [].display == prop - you have to iterate over the set and set the display for each element.
document.getElementsByClassName('truck'); always returns the array list of all the elements that has the class "track", so even if you have a single element that has the class "truck" the getElementsByClassName('truck') would return the single element in an array.
Please note, get**Elements**ByClassName has the plural form of element which refers to a set of element items to be fetched.
For your case, you have to iterate the array items to get each element and perform operations.
Try the following code block instead
<script>
function toggle() {
var e = document.getElementsByClassName('truck');
// Get all the elements that has the class name 'truck', so for your example,
// there are 2 items and the array e would have 2 items (i.e. e[0] will have the
// div element that has 'This is text that should hide' as the inner HTML and e[1]
// will have the div element that has [sc:1 ] as the inner HTML
for (var x=0; x < e.length; x++) {
if(e[x].style.display == "block") // Get the xth element's style
e[x].style.display = "none";
else
e[x].style.display = 'block';
}
}
</script>
Please note, even if you have a single item with the class name "truck" you would find it on the 0th location of the returned array (i.e. e[0]).

Categories