I am working on an API where the plan is that the users fill in a short checklist with multiple options before a POST request is made. For each section the users gets multiple options / buttons, but only 1 of them can be selected.
The selected button will get the class marked (Which changes the background color to green) whilst the others remain white (unless one of them is clicked, in which case it turns green and the others become white)
I have tried two different Javascript functions, but so far none of them were able to get this behavior to work.
Attempt 1:
function Label(self, group) {
// mark button
let btns = document.getElementsByClassName(group);
for (el in btns) {
btns[el].classList.remove('marked')
}
self.classList.add('marked');
}
Attempt 2 (a more explicit check to see if self is involved)
function Label(self, group) {
// mark button
let btns = document.getElementsByClassName(group);
for (el in btns) {
if (btns[el] !== self) {
btns[el].classList.remove('marked')
}
}
self.classList.add('marked');
}
My reasoning was to first remove the .marked class from all elements, and then set it on the this element. As the function is called with the onclick command in the HTML it knows which of the elements it is.
<div class="group1">
<button onclick="Label(this, pt_toggle)" class="pt_toggle">Incorrect</button>
<button onclick="Label(this, pt_toggle)" class="pt_toggle">Partially correct</button>
<button onclick="Label(this, pt_toggle)" class="pt_toggle">Correct</button>
</div>
However, the functions did not behave as I hoped. It throws an Uncaught TypeError: Cannot read property 'remove' of undefined error and the class is not set.
Does anyone know what I am doing wrong?
You're running into a feature of the for..in loop syntax. When you perform:
for (el in btns) {
if (btns[el] !== self) {
btns[el].classList.remove('marked');
}
}
every property in the btns object will be iterated over, including the length property which you won't be able to remove a class name from btns["length"].
If you switch to using a normal for loop using btns.length as the limit, you won't iterate over the non-button element properties in the object:
for (var el = 0; el < btns.length; el++) {
if (btns[el] !== self) {
btns[el].classList.remove('marked');
}
}
There is an element which performs this same kind of thing natively: the radio button. This makes it easier to code a more concise solution (well, except the CSS to make it look like more a button, but that's optional):
var buttons = Array.from(document.querySelectorAll(".group1 .button"));
buttons.forEach((el) => {
el.addEventListener("click", function (e) {
buttons.forEach((button) => button.classList.toggle("marked", button.querySelector("input").checked));
});
});
/* Hide the radio button */
.button input { display: none; }
/* make it green when selected */
.button.marked {
background: green
}
/* make the label look more like a button */
.button {
font: caption;
font-size: smaller;
padding: 2px 6px;
border: 1px solid #999;
border-radius: 1px;
background: white;
}
.button:hover {
border-color: ThreeDDarkShadow;
}
<div class="group1">
<label class="button"><input type="radio" name="pt_toggle">Incorrect</label>
<label class="button"><input type="radio" name="pt_toggle">Partially correct</label>
<label class="button"><input type="radio" name="pt_toggle">Correct</label>
</div>
Adding "marked" class on click to your button:
Your html:
<button onclick="Label(event)" class="pt_toggle">Incorrect</button>
Your js:
function Label(event) {
event.target.classList.add("marked");
}
Related
Anyways I’m trying to make a lightbulb. It’s just a circle, I have an onclick event on a button, and the event function toggled a class, changing the color. But I want the text in the button to toggle as well when that class is activated.
I finally made it work as I was writing this question. But it still isn’t working how I want it. I only want to have one if statement.
I only want to solve it this specific way, by checking if a class is activated, and if yes, then change the text content of the button, if not, then leave it how it is.
let bulb = document.getElementById("light");
let lightSwitch = document.getElementById("switch");
function activity(event) {
bulb.classList.toggle("lightOn");
if (bulb.classList.contains("lightOn")) {
lightSwitch.textContent = "OFF";
} else if (bulb.classList.contains("lightOff")) {
lightSwitch.textContent = "ON";
}
}
.lightOn {
background: yellow;
}
<div id="light" class="lightOff"></div>
<button id="switch" onclick="activity()">ON</button>
How can I write it with only one if statement, Also, is there a way easier than this? Just vanilla not jQuery.
like this?
...
function activity(event) {
bulb.classList.toggle("lightOn");
lightSwitch.textContent = bulb.classList.contains("lightOn") ? "OFF" : "ON";
}
...
Your code can be simplified by extracting the bulb-specific default styles into a light class, then only toggle a new lightOn class. The default styles describe what to show if the bulb is off, but you can override those styles with !important in the lightOn class. Just like this:
let bulb = document.getElementById("light");
let lightSwitch = document.getElementById("switch");
lightSwitch.addEventListener('click', function() {
bulb.classList.toggle('lightOn');
lightSwitch.textContent = bulb.classList.contains("lightOn") ? "OFF" : "ON";
});
.light {
width: 100px;
height: 100px;
border-radius: 100%;
background: gray;
}
.lightOn {
background: yellow !important;
}
<div id="light" class="light"></div>
<button id="switch">ON</button>
You can also shorten your if-statement into a ternary expression, which is a better choice for this situation because only one value changes.
I am making an etch-a-sketch and I am trying to use an addEventListener event of 'mouseover' to add a CSS class where the background of the square the mouse hovers over will turn black. I have a function that creates a single box and if I put the event listener inside this function it works, however if I try to do it outside the function it doesn't work
The function box creates a single box (which will get repeated using the addMultipleBox function) and adds a mouseover event. In this scenario the mouseover works correctly
function box() {
let square = document.createElement('div')
square.setAttribute('class', 'box')
container.appendChild(square)
square.addEventListener('mouseover', () => {
square.classList.add('blackPen')
})
}
//creates the etch a sketch board with multiple 'boxes'
function addMultipleBoxes() {
for(let i = 0; i < 256; i++) {
box()
}
}
Now if I try and grab the class of 'box' outside the function and add an event listener to it nothing happens. I do have this code at the bottom so it's not like i'm trying to grab divs before they are created.
I'd like to be able to grab it outside so I can create another function that on a mouse click I remove the class of 'blackPen' which will remove the background color of black on the square, essentially wiping the board clean. Here is what I have for that
let boxx = document.querySelector('.box')
console.log(boxx)
boxx.addEventListener('mouseover', () => {
boxx.classList.add('blackPen')
})
When I console.log 'Boxx' I get the <div class="box"></div>. If I console.log "square" above in the box function I get the same thing as Boxx.
Any insight would be much appreciated!
The box() function adds the class to each element as it is made, as required.
A reference made to an element using querySelector contains only one element - the first in the document with the specified selector. See https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
This snippet illustrates the idea with four divs, all the same class.
let boxx = document.querySelector('.box')
boxx.addEventListener('mouseover', () => {
boxx.classList.add('blackPen')
})
.box {
display: inline-block;
width: 70px;
aspect-ratio: 1;
background: yellow;
border: 1px solid red;
}
.blackPen {
background: black;
}
<p><b>mouseover applied to element got by querySelector</b></p>
<p>(move mouse over divs)</d>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<p>querySelector, returns the <em>first</em> Element within the document that matches the specified selector</p>
A reference to all elements with a given class can be made using querySelectorAll(), which returns a live node list with references to all the elements of the given selector. See: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
Having made the reference, you will still have to loop through them in order to add the event listeners required. node lists and html collections are array-like structures that can have individual members referenced by and index [0],[1], and so on.
This snippet illustrates adding a new class to each div in a node list formed using querySelectorAll:
let boxx = document.querySelectorAll('.box')
for (let i=0; i<boxx.length; i++) {
boxx[i].addEventListener('mouseover', () => {
boxx[i].classList.add('blackPen');
boxx[i].classList.remove('cancel');
});
boxx[i].addEventListener('click', () => {
boxx[i].classList.add('cancel')
});
} // next i boxx element;
.box {
display: inline-block;
width: 70px;
aspect-ratio: 1;
background: yellow;
border: 1px solid red;
}
.blackPen {
background: black;
}
.cancel {
background: yellow;
}
<p><b>mouseover applied to all elements got by querySelectorAll</b><br>(move mouse over divs)</d>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<p>A click event was also added to all elements, to replace the class added by the mouseover event<br>click boxes to see the effect</p>
Note that the node collection only needs to be made once to be used for any further manipulations. In the snippet a mouseover event adds a class to make the background black while a click event cancels it.
I have a site with a lot of different div. The thing they have in common is all share (besides their unique classes) a shared class. Lets just call it .changeClass.
What I am looking for is code with a button (or radio buttons) and by clicking the button, the background instance of all these divs will get the same one (which the .changeClass has). So the .changeClass will just be active when the button is toggled/clicked.
I am looking for a way to do this with pure javascript and no Jquery.
Sorry for being a noob :-)
In the solution below, clicking the <button> element will add/remove the class style .changeClass to all elements that have the class style .apply applied.
let button = document.getElementById('change');
let containers = document.getElementsByClassName('apply');
function changeButtonText() {
if(button.innerHTML === "Add")
button.innerHTML = "Remove";
else
button.innerHTML = "Add";
}
button.addEventListener('click', function() {
for(let index = 0 ; index < containers.length ; ++index)
containers[index].classList.toggle('changeClass');
changeButtonText();
});
div {
margin-top: 25px;
}
.apply {
border: 3px solid black;
}
.changeClass {
background-color: black;
color: white;
border: 3px solid red;
margin-top: 25px;
}
<button id="change">Add</button>
<div class="apply">1</div>
<div class="apply">2</div>
<div class="apply">3</div>
<div class="apply">4</div>
<div class="apply">5</div>
First lets get all divs that are on the DOM
const divs = document.getElementsByTagName("div");
You will have array of all the divs that are on the DOM. Then add your class to all of it. In order to do that, lets loop it.
divs.forEach(div => div.className += div.className + " changeClass");
Could this be what you are looking for?
In html:
<button onclick="changeColor('blue');">blue</button>
In JS
function changeColor(newColor) {
var elem = document.getElementsByClassName("changeClass");
elem.style.color = newColor;
}
The HTML color can be any color you would like it to be, just change they name from blue to any color or input a hex code.
We have multiple divs with the same class value
We have given a function to the button that we want the event to happen when it is clicked, using the onclick method. Now when we click the button, the function called myFunction will run.
HTML:
<div class="changeClass">Im Example Div</div>
<div class="changeClass">Me Too</div>
<button type="submit" onclick="myFunction()">Click To Change Div BgColors !
</button>
We must define myFunction as Javascript and change the background color.
We have defined a function called myFunction.
With the getElementsByClassName selector in our function, we got all the data with the class value changeClass in object format.
To add a background (or any css property) to all of these objects; We put the object in a for loop and now we split our elements.
We can now define a background color for our elements with the style.backgroundColor parameter.
JavaScript:
function myFunction(){
var divs = document.getElementsByClassName('changeClass');
for(var i=0; i< divs.length; i++){
divs[i].style.backgroundColor = 'red';
}
}
For more detailed information, you can refer to the resources: https://www.w3schools.com/jsref/met_document_getelementsbyclassname.asp
Don't be sorry for being new at something and wanting to learn more!
So what you are saying is that the divs you want to change all have a common class of "changeClass". If this is the case then you want a function is passed an argument value of the color you want to be changed. Since all of your divs are static and you probably don't plan on changing, declare a variable outside of this function that has the following code
const divs = document.getElementsByClassName("changeClass")
Then, inside of the function, loop through all of the divs collected inside the variable "divs", or whatever you want to call it. Since "getElementsByClassName" returns a collection, it does not have the built in "foreach" and "map" methods. So you have to use a for loop preferably the following.
const divs = document.getElementsByClassName("changeClass");
function changeColor(color) {
for (let element of divs) {
element.style.backgroundColor = color;
}
}
I may have interpreted this wrong but I hope it helps
You may find using a CSS variable helpful.
For example:
function bg(color) {
document.body.style.setProperty('--bg', color);
}
body {
--bg: cyan;
}
.container {
display: flex;
gap: 1vw;
}
.container div {
width: 100px;
height: 100px;
background-color: black;
}
.container div.changeClass {
background-color: var(--bg);
}
<body>
<button onclick="bg( 'red');">Red</button>
<button onclick="bg( 'green');">Green</button>
<button onclick="bg( 'blue');">Blue</button>
<button onclick="bg( 'black');">Black</button>
<div class="container">
<div class="changeClass"></div>
<div class="changeClass"></div>
<div class="changeClass"></div>
<div></div>
<div class="changeClass"></div>
</div>
</body>
Then when one of the radio buttons is clicked it sets the variable --bg.
Here's a simple snippet:
First of all - thank you for all your replies. And yes I should have included code. I tried so many things that i just gave up at som point - got confused what was right code and what was just rubbish. So I appreciate so much that you all took time to answer me. This was my first post so now I know for the future. The answers I got all was possible ways to solve my problem - so thank you all. I will do better next time. You are awesome...
BTW - All solutions seems to work - but can only checkmark one of them as you know.
You can add or remove a class to change the colours of different div:
document.queryselector('.className').classList.add('classNamethatyouwanttoadd');
document.queryselector('.className').classList.remove('classNamethatyouwanttoadd');
I have different buttons without id like this:
<div id="buttons">
<button data-note="n1">One</button>
<button data-note="n2">Two</button>
</div>
I cannot access them by .button since I have many, like 20 or so. Can I access them by their data-note attribute? querySelector selects the first one, second or all of them. Can I specifically select them, maybe put them an id?
What I actually want is to store the data-note as an Array, so have to convert that data into arrays as list to add to the HTML.
I supposed it is also possible without the id?
I might have to create for each button a function that by clicking them it stores that information as array into a list that I can access later if called, but I don't know how to select these children. If possible not by nodelist numbers, but by data, or if by nodelist then.
It might be a problem depending on the browser as they count 0,1,2 or 1,3,5... Is it possible to use getElementById and select a specific number of child?
To addEventListener I need to select each one of the buttons.
If you just want the notes data in a unordered list, then you can do the following:
// Get all the buttons
let buttons = document.getElementsByTagName("button");
// Get the "clear" button as it has a special function
let clear = buttons[buttons.length - 1];
// Create an unordered list
let ul = document.createElement("ul");
// Iterate through the buttons
for (let button of buttons) {
// Add the event listener to each button
button.addEventListener("click", (e) => {
// Clear the unordered list if the clear button was pressed
if (e.target === clear) {
ul.innerHTML = "";
return;
}
// For each button create a list item
let li = document.createElement("li");
// Set it's innerText attribute to be the data-note
li.innerText = `${button.dataset.note} - ${e.target.style.backgroundColor}`;
// Append it to the unordered list
ul.append(li);
});
}
// Append the list to the body
document.body.append(ul);
button {
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
}
<h2>Buttons Game</h2>
<p>Click the buttons to add them to the list, press clear to reset</p>
<button data-note="n1" style="background-color: red;">red</button>
<button data-note="n2" style="background-color: green;">green</button>
<button data-note="n3" style="background-color: orange;">orange</button>
<button data-note="n4" style="background-color: blue;">blue</button>
<button id="clear" style="color: black; border: 1px solid black;">clear</button>
Sample Output:
Steps:
1) Use getElementsByTagName to store all the buttons into an array
2) Use a for loop to loop through the array
3) Use setAttribute to set the ids for all the buttons
4) Use the allButtons array as you wish since it contains all the buttons already, with their ids set.
Code:
var allButtons = document.getElementsByTagName("button");
for (var i = 0; i < allButtons.length; i++) {
allButtons[i].setAttribute("id", "button-" + [i])
}
console.log(allButtons);
<div id="buttons">
<button data-note="n1">One</button>
<button data-note="n2">Two</button>
</div>
This can be done easily with
var myNodes = document.querySelectorAll("button[data-note]");
This will assign to myNodes all buttons that contain data-note.
One of the properties of the array created's elements is "attributes" so you can search them easily with .find
Note: I see that you are actually trying to record the clicks, in order, like a Simon game. In that case you should have a hidden div on the page to store the data in, have every click call your event and that event gets the relevant control's data-note from the passed in e object to the function. You can assign as many click events as necessary to the control and they will execute in reverse order, last one added runs first.
For example, I have an HTML form element with a textarea, and two buttons as child elements. How can I detect when the focus goes from one of those three elements, to an element OTHER THAN one of those three. In other words, how do I detect when focus leaves the form, so I can dismiss it automatically?
I thought I could use the focusout event on the form to figure out when focus no longer belonged to one of its child elements, but the object gaining the focus doesn't seem to be accessible from the focusout event, so I have no way to checking whether focus is just going from the textarea to one of the buttons, for example.
The relatedObject, fromElement, and toElement properties are all undefined.
While i fear this approach may have subtle errors, it's the best i know of:
register on the form's focusout event (not blur, because the latter does not bubble)
register a 0-delayed function via setTimeout, so the focus transfer is handled
in the timeouted function, check if document.activeElement is a descendent of your form. If not, fire.
Here is a example implementation:
$('#yourform').on('focusout', function(evt) {
var self = $(this);
setTimeout(function() {
if (self.has($(document.activeElement)).length == 0) {
alert('leave');
}
}, 0);
});
form {
border: 1px solid red;
margin: 1em;
padding: 1em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id='yourform'>
<input type='text'>
<input type='text'>
</form>
<input type='text'>
const form = document.querySelector(".myform");
document.onclick = () => {
// query selecting the form element and checking for the
// css :focus pseudo class inside the form element
if (!form.querySelector(":focus")) {
// this console log would run when any placeholder in the form is not focused
console.log("form dismissed.")
}
}
.myform {
padding: 10px;
background: blueviolet;
color: #fff;
}
.myform * {
display: block;
margin: 5px 0;
}
<form class="myform">
when not selecting the placeholders this will log a msg to the console
<input value="select here" type="text"/>
<input value="select here" type="text"/>
<textarea>Select here</textarea>
<button>Submit</button>
</form>
As you noticed in the snippet above you could use the element.querySelector(":focus") to check whether the contents of the form has focus or not. This approach completely works fine.
( vote if you found this answer useful to you )
correct name is "blur"
function setBlur()
{
var elements = document.getElementByTagName('input');
for(var i = 0; i < elements.length; i++)
elements[i].setAttribute('onfocus', 'callme(this)');
}
function callme(el)
{
var _id = el.getAttribute('id');
if(_id != "textarea_id' && _id != "button_1_id" && _id != "button_2_id")
callYourMethodToHandleFocusOutsideYourForm();
}
body.onload = function() { setBlur(); }
this will do the trick
you can do it also for textarea just apply document.getElementByTagname('textarea') and loop through array.