Quick question here, I encountered this problem today while practicing some JS. I wanted to create a basic prototype to loop through a "div" background-color array on click, but I realized that assigning the element property to a variable (instead of using the event target) impedes me to change the actual values.
This is the JS code:
let colors = ["blue", "yellow", "orange", "red"]
let n = 1;
document.querySelectorAll('div').forEach(occurence => {
occurence.addEventListener('click', (e) => {
let classes = e.target.className;
classes = colors[n];
n++;
console.log(classes);
if (n >= 4) {n = 0;}
});
});
So, changing the actual e.target.className works just fine, but trying to change the assigned "classes" variable does nothing. I feel like this may be a matter of specificity, or JS being unable to access the actual property values, or some akin beginner mistake.
e.target.className passes by value when you have let classes = e.target.className, so classes contains a copy of its data. Changing classes just changes the copy, rather than what's stored in e.target.classname.
Actually, you are not changing the value of e.target.className. What you do, is assigning the value of e.target.className to the variable/let-binding classes. To assign one of the color values to the className property, the assignment has to be the other way around:
e.target.className = colors[n];
let classes = e.target.className will assign the current string value of className to classes. And while you can assign a new colour value to classes that won't assign the new colour value to the className property of the element. For that you want to explicitly assign it: e.target.className = colors[i].
You may also want to remove the need to add a event listener to all the elements. Event delegation allows you to add one listener to a parent element which captures events from its child elements as they "bubble up" the DOM.
Here's an over-wrought example:
const colors = ['blue', 'yellow', 'orange', 'red'];
// Cache the elements first, and add a listener to
// the container
const counter = document.querySelector('.index');
const container = document.querySelector('.container');
container.addEventListener('click', handleClick);
let count = 0;
function handleClick(e) {
// Check to see if the element that was clicked
// was a div element
if (e.target.matches('.container div')) {
// Update the counter element, the `className` value,
// and the `textContent of the "box", and then update
// the count value
counter.textContent = `Color index: ${count}`;
e.target.className = colors[count];
e.target.textContent = colors[count];
count = count < colors.length - 1 ? ++count : 0;
}
}
.container { display: grid; gap: 0.4em; grid-template-columns: repeat(2, 50px); }
.container div { display: flex; justify-content: center; align-items: center; height: 50px; width: 50px; border: 1px solid lightgray; }
.container div:hover { cursor: pointer; border: 1px solid darkgray; }
.blue { background-color: lightblue; }
.yellow { background-color: #ffff00; }
.orange { background-color: darkorange; }
.red { background-color: red; }
.index { margin-top: 0.5em; }
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="index">Color index:</div>
Related
i am trying to learn JS, React and Node.js. Now i am trying to change the width of a div with a button but i'm getting always the same error, that it "Cannot read property 'style' of null
Here is my JS
var sideNav = document.getElementById("side-nav");
var linksSideNavTop = document.getElementById("links-side-nav-top");
var linksSideNavBottom = document.getElementById("links-side-nav-bottom");
function openMenuTablet() {
if (sideNav.style.width === '70px') {
sideNav.style.width = '300px';
linksSideNavTop.style.display = "block";
linksSideNavBottom.style.display = "block";
} else {
sideNav.style.width = '70px';
linksSideNavTop.style.display = "none";
linksSideNavBottom.style.display = "none";
};
}
Here the CSS
#side-nav {
position: fixed;
background-color: #454545;
height: 100%;
width: 70px;
}
Have a look at this working example:
function changeWidth() {
document.getElementById('myContainer').style.width = '500px'
}
#myContainer {
width: 250px;
height: 50px;
background: #aaa;
}
<div id="myContainer"></div>
<button onclick="changeWidth()">Change width</button>
Above answers Are correct, also I can show my example how to do that by .classList.toggle()
const box = document.querySelector(".box")
const button = document.querySelector("button")
button.addEventListener("click", () => {
box.classList.toggle("toggle")
})
.box{
width: 100px;
height: 100px;
background: red;
margin-bottom: 10px;
transition: 0.5s
}
.box.toggle{
width: 200px;
}
<div class="box"></div>
<button>Toggle Class List</button>
if your class list has toggle your div width increases and and in Javascript .toggle method removes and adds your passed classList which is "toggle" in our case
Are you sure you have declared the items with the id´s of links-side-nav-top, links-side-nav-bottom and side-nav?
You can try to console.log(linksSideNavTop); to check existence of id.
The error "Cannot read property 'style' of null" means that the code to get the element (document.getElementById()) isn't retrieving (founding) an element with that particular ID.
I would suggest for you to double check that you don't have duplicated ID with the same exact name and also to move the document.getElementById() inside your function.
function openMenuTablet() {
var sideNav = document.getElementById("side-nav");
var linksSideNavTop = document.getElementById("links-side-nav-top");
var linksSideNavBottom = document.getElementById("links-side-nav-bottom");
if (sideNav.style.width === '70px') {
// ...
} else {
// ...
};
}
Also a good alternative to document.getElementById() would be document.querySelector():
document.querySelector("h1"); // Search html tag "h1"
document.querySelector(".rect"); // Search class name "rect"
document.querySelector("#square"); // Searcd id name "square"
For last, to know if you are really founding and retrieving the intended element you can for example console.log() the var element:
var sideNav = document.getElementById("side-nav");
console.log(sideNav);
// ...
I try to make a simple like/dislike function to my page. The image changing is working but the counter not and I do not know why. Any idea how to make it workable?
By the way I have read a bunch of questions about like/dislike system with JS but its not really works for me.
const imageChange = document.querySelector('.likeClassQ')
var countL = 0;
var buttonLike = document.getElementById("likeButton");
var displayLike = document.getElementById("likes");
buttonLike.addEventListener('click', () => {
imageChange.classList.toggle('likeClassFilled')
})
buttonLike.onclick = function() {
if (buttonLike.classList == 'likeClass') {
countL++;
buttonLike.classList.add = "likeClassFilled";
} else if (buttonLike.classList == "likeClassFilled") {
countL--;
buttonLike.classList.add = "likeClass";
}
displayLike.innerHTML = countL;
}
.likeClass {
background-color: red;
background-repeat: no-repeat;
background-size: contain;
padding: 10px;
border: none;
}
.likeClassFilled {
background-color: green;
}
<span><span id="likes">0</span> Likes</span><br>
<button id="likeButton" class="likeClass likeClassQ">Like</button>
There is no need to assign a function to onclick and use addEventListener. Just use one and stick to it.
Your CSS classes are all over the place. Use one for the general styling and another one for your state. Or better yet, use the data attribute if the element or maybe even a stylized checkbox for that. Mixing CSS classes and business logic is a slippery slope.
classList has methods like toggle, add and includes, but you have to use those fields as methods and not as simple fields. Also, you can not use the comparison operator (==) with objects. You would only use that on simple values like strings or numbers. You execute functions/methods by writing brackets after the method/function name and passing any parameters in those. When you use the assignment operator (=), you are not calling anything.
Your logic about saving the state and deriving the current state is flawed. I changed it to toggle a class on each click. Hence you will not find any classes being added or removed within the condition.
const imageChange = document.querySelector('.likeClassQ')
var countL = 0;
var buttonLike = document.getElementById("likeButton");
var displayLike = document.getElementById("likes");
buttonLike.onclick = function() {
if (buttonLike.classList.contains('likeClassFilled')) {
countL--;
} else {
countL++;
}
buttonLike.classList.toggle("likeClassFilled");
displayLike.innerHTML = countL;
}
.likeClass {
background-color: red;
background-repeat: no-repeat;
background-size: contain;
padding: 10px;
border: none;
}
.likeClassFilled {
background-color: green;
}
<span><span id="likes">0</span> Likes</span><br>
<button id="likeButton" class="likeClass likeClassQ">Like</button>
This question already has answers here:
How can I change an element's class with JavaScript?
(33 answers)
Closed 2 years ago.
I have a button whose original style is display: none, and after activating a function i want to change his style to an appear using style of defined class style.
I have the pause button as follows:
<li><button className="pause_button"
onClick={() =>{this.props.pause_resume();}}>
</button>
</li>
and the styles
.sortButton {
background-color: ivory;
color: black;
padding: 10px 25px;
border-radius: 3px;
margin: 5px;
font-size: 16px;
opacity: 1;
}
.sortButton:hover {
background-color: dodgerblue;
color: white;
opacity: 1;
}
.pause_button {
display: none;
}
I am trying to change the style of pause_button to be the style of sortButton
Here is what i tried, but unfortunately no change appears.
const pause_button = document.getElementsByClassName("pause_button");
pause_button.style = "sortButton";
In a different part of my code I'm using the same way and it does make changes to the button.
const buttons = Array.from(document.getElementsByClassName("sortButton"));
buttons.forEach(button => {
button.style = "sortButton";
})
The last bit of code happens after I have changed the buttons style in this way.
const buttons = Array.from(document.getElementsByClassName("sortButton"));
buttons.forEach(button => {
if(button.innerText !== text){
button.style.backgroundColor = 'grey';
button.style.opacity= 0.2;
}
})
All the changes except from the pause button appearing works,
Two things: style is an object, not a string. You need to change the element's class instead. And getElementsByClassName returns a collection of elements. In your code:
const pause_button = document.getElementsByClassName("pause_button");
pause_button.style = "sortButton";
pause_button is an HTMLCollection. You can use Array.from() like you did in the other example, or you can iterate over it directly using for...of.
const buttons = getElementsByClassName("pause_button");
for (button of buttons) {
button.className = "sortButton";
}
You have to use Element.className on the DOM Node to associate a class to a particular node. To apply the class "sortButton" you have to do the following:
const buttons = Array.from(document.getElementsByClassName("sortButton"));
buttons.forEach(button => { button.className = "sortButton" });
So this the code I'm doing
<a onclick="setStyleSheet('css/blue.css')" href="#">Switch</a>
<a onclick="setStyleSheet('css/red.css')" href="#">Switch</a>
On click it'll switch to blue theme.
But I want to keep the button same.
Instead of using 2 buttons. I just want one Switch button to change to blue, then if I click that button again, it'll change to red.
Is there anyway to do this?
THanks all in advance!
You can set a global flag
window.blueTheme = true
function setStyleSheet() {
var styleSheetToBeSet = window.blueTheme ? "css/red.css" : "css/blue.css"
window.blueTheme = !window.blueTheme
// your code here
}
Of course, you can change blueTheme to theme and store theme name instead of boolean variable.
And then just call the function without parameter:
<a onclick="setStyleSheet()" href="#">Switch</a>
Simple, use if loop to compare the argument like
function StyleSheet(arg) {
if (arg == "css/blue.css") {
var styleSheetToBeSet = "css/red.css";
}
else {
var styleSheetToBeSet = "css/blue.css";
}
//set style sheet here with js
}
I would use another helper function and set some condition first.
function setElement(){
var style = document.styleSheets[0]; //set the index
if(style.href ==='css/blue.css'){
style.href = 'css/red.css';
}else if (style.href ==='css/red.css'){
style.href = 'css/blue.css';
}else{
console.log('error');
}
}
<a onclick="setElement();" href="#">Switch</a>
I suggest not to use the href="#". This will give uggly urls.
If you use the anchor better use
<a onclick="setStyleSheet('....')" href="javascript:void(0)">Switch</a>
Another option is using event.preventDefault(); in your javascript function.
One approach, that may work (depending upon the complexity of your stylesheets) is to update a CSS custom property to update the properties you wish to change or edit:
// defining the colours to be used (the approach taken in these
// code snippets allows for other values to be added to this
// Array to extend the number of colours/values used):
let cssStyles = ['blue', 'red'],
// defining the function (using Arrow syntax, since
// we don't need to use the 'this' object within):
modifyCSS = (styles) => {
// 'styles': the Array of colours passed into the function.
// here we find the root-element of the document:
let root = document.querySelector(':root'),
// we retrieve the current value of the '--colorValue'
// CSS property defined in that Node's CSS (if present):
current = root.style.getPropertyValue('--colorValue'),
// and, because this property-value is a String, we search
// the Array of colours to retrieve the index of the current
// colour:
index = styles.indexOf(current);
// here we update the '--colorValue' custom property to the
// property held in the next index of the Array; or to the
// value held at the Array's zeroth/first index:
root.style.setProperty('--colorValue', styles[++index % styles.length]);
}
// binding the function to the click event on the '#updatecss' button:
document.querySelector('#updatecss').addEventListener('click', (e) => modifyCSS(cssStyles) );
let cssStyles = ['blue', 'red'],
modifyCSS = (event, styles) => {
let root = document.querySelector(':root'),
current = root.style.getPropertyValue('--colorValue'),
index = styles.indexOf(current);
root.style.setProperty('--colorValue', styles[++index % styles.length]);
}
document.querySelector('#updatecss').addEventListener('click', (e) => modifyCSS(e, cssStyles) );
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.wrapper {
display: grid;
grid-template-columns: 40vw 40vw;
width: 80vw;
margin: auto;
grid-gap: 4px;
}
.wrapper>div {
color: var(--colorValue, black);
border: 2px solid currentColor;
grid-column: 1/-1;
}
<div class="wrapper">
<button id="updatecss">Switch CSS</button>
<div>element 1</div>
<div>element 2</div>
<div>element 3</div>
<div>element 4</div>
</div>
JS Fiddle demo.
Given that your question requires new stylesheets, and is probably a simplified version of your requirements, it may be more difficult to update CSS custom properties to meet your needs. However, we can use a similar approach with an array of stylesheet names:
// CSS stylesheet filenames:
let stylesheets = ['blue.css', 'red.css'],
// named function, using Arrow syntax as above, to change
// the stylesheets:
modifyCSS = (sheets) => {
// finding the relevant <link> element:
let link = document.querySelector('.linkedResource'),
// finding the index of the last '/' character in
// the href property-value of the <link>; adding 1
// so that the last slash is included in the 'path'
// and not the 'file':
lastSlashIndex = link.href.lastIndexOf('/') + 1,
// here we find the substring of the href up to,
// and including, the last '/' character:
path = link.href.substring(0, lastSlashIndex),
// finding the filename (based on the assumption
// that the filename follows the last '/' character):
file = link.href.slice(lastSlashIndex),
// finding the index of the current filename in the
// Array of filenames:
currentSheetIndex = sheets.indexOf(file);
// updating the href of the <link> element to be equal
// to the concatenated value of the path and the
// filename held at the next, or first, index of the Array:
link.href = path + sheets[++currentSheetIndex % sheets.length];
};
document.querySelector('#updatecss').addEventListener('click', () => modifyCSS(stylesheets));
let stylesheets = ['blue.css', 'red.css'],
modifyCSS = (sheets) => {
let link = document.querySelector('.linkedResource'),
lastSlashIndex = link.href.lastIndexOf('/') + 1,
path = link.href.substring(0, lastSlashIndex),
file = link.href.slice(lastSlashIndex),
currentSheetIndex = sheets.indexOf(file);
link.href = path + sheets[++currentSheetIndex % sheets.length];
};
document.querySelector('#updatecss').addEventListener('click', () => modifyCSS(stylesheets));
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.wrapper {
display: grid;
grid-template-columns: 40vw 40vw;
width: 80vw;
margin: auto;
grid-gap: 4px;
}
.wrapper>div {
color: var(--colorValue, black);
border: 2px solid currentColor;
grid-column: 1/-1;
}
<link rel="stylesheet" href="https://www.davidrhysthomas.co.uk/linked/blue.css" class="linkedResource" />
<div class="wrapper">
<button id="updatecss">Switch CSS</button>
<div>element 1</div>
<div>element 2</div>
<div>element 3</div>
<div>element 4</div>
</div>
JS Fiddle demo.
References:
CSS:
CSS Custom Properties (--*).
CSS Grid Layout.
JavaScript:
Array.prototype.indexOf().
CSSStyleDeclaration.getPropertyValue().
CSSStyleDeclaration.setProperty().
document.querySelector().
document.querySelectorAll().
JavaScript remainder (%) operator.
String.prototype.lastIndexOf().
String.prototype.slice().
String.prototype.substring().
Bibliography:
Constantly loop a javascript array and display results to div?
As the title says I am wondering if it is possible to loop through the style attributes of a div with javascript or jquery. What I want to do is loop through the styles and create an object containing these style names and values.
Here is an example of what I am trying to do:
alert($('#mydiv').attr('style'));
Gives the following:
background-color: #CCCCCC; border-width: 2px; border-style: solid; width: 250px;
And I want to create a object which looks like this:
{"background-color":"#CCCCCC","border-width":"2px","border-style":"solid","width":"250px"}
What I can't figure out is whether this is achievable by looping through the styles or whether I will have to create the object myself using code similar to below:
var style = {};
style['width'] = $('#mydiv').css('width');
Any input of this would be appreciated.
Version 1, using inline style
const style = $("#myDiv").attr("style");
const parts = style.split(";")
console.log(parts)
let obj = {}
parts.forEach(part => {
if (part.length > 0) { // skip the empty element after the last ;
const [key,val] = part.split(':');
obj[key] = val.trim();
}
})
console.log(obj)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="myDiv" style="background-color: #CCCCCC; border-width: 2px; border-style: solid; width: 250px;">My div</div>
Version 2 using computed style of a list of elements
// more code needed to handle rgba
const rgbToHex = rgb => '#' + (rgb.match(/[0-9|.]+/g).map((x, i) => i === 3 ? parseInt(255 * parseFloat(x)).toString(16) : parseInt(x).toString(16)).join('')).padStart(2, '0').toUpperCase();
let obj = {}
const style = window.getComputedStyle(document.getElementById('myDiv')); //
["background-color", "border-width", "border-style", "width"]
.forEach(rule => {
const val = style.getPropertyValue(rule)
obj[rule] = val.includes('rgb') ? rgbToHex(val) : val;
})
console.log(obj)
#myDiv {
background-color: #CCCCCC;
border-width: 2px;
border-style: solid;
width: 250px;
}
<div id="myDiv">My div</div>
For <div id="id" style="color: red"/>, $('#id').attr('style') will return a string color: red so i guess, you can't loop them directly.
However you can create an array string.split(';') and loop over them.
But CSS associated with in inside <style> tag or some css file, i don't think you can get it. However, I am not sure.