Button styling bug - javascript

I want to receive information from my users using buttons, so i created two sections for that. When the user clicks a button i need it to change its style so the user will see a difference in what they clicked even when they click another button in the other section. The problem is that when another button is clicked the other section looses its style (so the user wouldn't know what he previously clicked). I pasted the html code of the part with the problem(below)
<div>
<p><strong>Network</strong></p>
<button class="btn25" onclick = "gfg_Run()">
MTN
</button>
<button class="btn25">
Glo
</button>
<button class="btn25">
9 Mobile
</button>
<button class="btn25">
Airtel
</button>
</div>
<div>
<p><strong>Data</strong></p>
<button class="btn25">
1Gb
</button>
<button class="btn25">
2Gb
</button>
<button class="btn25">
3Gb
</button>
<button class="btn25">
5Gb
</button>
<button class="btn25">
10Gb
</button>
</div>

i need it to change its style so the user will see a difference
Create a class called active and then set the buttons to use that class when the user selects them.
The problem is that when another button is clicked the other section looses its style
The issue is that you need to separate out each group. I added a class on the parent div that distinguishes the two groups: "data" or "speed". The button background will change to blue for each of the groups because it is limited to that class in the query as ".data" does here:
document.querySelectorAll('.data .btn25');
Here is an example snippet showing the basic technique.
const dataChoices = document.querySelectorAll('.data .btn25');
dataChoices.forEach(function(choice) {
choice.addEventListener('click', function() {
removeClass(dataChoices);
this.classList.add('active');
});
});
const speedChoices = document.querySelectorAll('.speed .btn25');
speedChoices.forEach(function(choice) {
choice.addEventListener('click', function() {
removeClass(speedChoices);
this.classList.add('active');
});
});
function removeClass(dataGroup) {
dataGroup.forEach(function(choice) {
choice.classList.remove('active');
});
}
.active {
background-color: lightblue;
}
div {
float: left;
margin-right: 10px;
}
<div class="data">
<p><strong>Data</strong></p>
<button id="1" class="btn25">1Gb</button>
<button id="2" class="btn25">2Gb</button>
<button id="3" class="btn25">3Gb</button>
<button id="4" class="btn25">5Gb</button>
<button id="5" class="btn25">10Gb</button>
</div>
<div class="speed">
<p><strong>Speed</strong></p>
<button id="1" class="btn25">1Mbps</button>
<button id="2" class="btn25">2Mbps</button>
<button id="3" class="btn25">3Mbps</button>
<button id="4" class="btn25">5Mbps</button>
<button id="5" class="btn25">10Mbps</button>
</div>

I have an alternate way to do this, but honestly Christopher Taleck's answer is perfectly fine as well, I just had a different conception of how to solve the problem and figured that would be informative for others facing a similar situation.
Here is a link to a JSFiddle with a working example: JSFiddle
Here is the complete code, an explanation of everything is provided below:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Button Practice</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="network-container">
<h2>Network</h2>
<button type="button" class="btn25">MTN</button>
<button type="button" class="btn25">Glo</button>
<button type="button" class="btn25">9 Mobile</button>
<button type="button" class="btn25">Airtel</button>
</div>
<div class="speed-container">
<h2>Speed</h2>
<button type="button" class="btn25">1Mbps</button>
<button type="button" class="btn25">2Mbps</button>
<button type="button" class="btn25">3Mbps</button>
<button type="button" class="btn25">5Mbps</button>
<button type="button" class="btn25">10Mbps</button>
</div>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
script.js
function ButtonContainer(element) {
this.element = element;
this.selectedButton = null;
this.element.addEventListener('click', function(e) {
if (e.target.type !== 'button') return;
if (this.selectedButton) this.selectedButton.classList.remove('active');
e.target.classList.add('active');
this.selectedButton = e.target;
});
}
const networkContainer = new ButtonContainer(document.querySelector('.network-container'));
const speedContainer = new ButtonContainer(document.querySelector('.speed-container'))
style.css
.active {
background-color: #9AD58E;
}
GENERAL CODE EXPLANATION
First, I would suggest removing this call to gfg_Run() from the .html file:
// index.html
<button class="btn25" onclick = "gfg_Run()">
And creating a separate file to hold your JS. Then, you can just link that file like so, right before the closing <body> tag (I created a file called script.js for this purpose):
// index.html
<script type="text/javascript" src="script.js"></script>
Also, I would suggest adding a descriptive class based on the type of buttons that each <div> holds, like so:
// index.html
<div class="network-container">
------ rest of the container ------
<div class="speed-container">
------ rest of the container ------
To handle the change of style on the button click, you can create a class that is added to the clicked button. I will use .active as that is a pretty common convention. This can be in a style.css file that you link from the head of the HTML document:
// style.css
.active {
background-color: #9AD58E;
}
In that script.js file, my approach would be to create a constructor function that represents the container that holds the buttons:
// script.js
function ButtonContainer(element) {
this.element = element;
this.selectedButton = null;
this.element.addEventListener('click', function(e) {
if (e.target.type !== 'button') return;
if (this.selectedButton) this.selectedButton.classList.remove('active');
e.target.classList.add('active');
this.selectedButton = e.target;
});
}
ButtonContainer CONSTRUCTOR FUNCTION EXPLANATION
We set the ButtonContainer up with two properties, element and selectedButton. element represents the DOM element which contains the buttons (in our case, a <div>), and the selectedButton represents the last <button> in this <div> that was clicked.
The eventListener will fire whenever the <div> is clicked anywhere inside it's borders. We only want to handle clicks on a <button> element within this <div>, so we exit the listener if the target of the event was not a button with the line below:
if (e.target.type !== 'button') return;
Next, we want to see of there is already a selectedButton in this <div>, and if so, we need to remove the .active class from it so we don't have two buttons in the same <div> that have colored backgrounds:
if (this.selectedButton) this.selectedButton.classList.remove('active')
Finally, we set the .active class on the <button> that triggered the event, and set the selectedButton property of this ButtonContainer object to that button, so that we can keep track of the currently selected button in this <div>:
e.target.classList.add('active');
this.selectedButton = e.target;
Then simply create two instances of the ButtonContainer that represent the <div> elements containing each group of buttons (each <div> should have a class representing the type of buttons it holds):
const networkContainer = new ButtonContainer(document.querySelector('.network-container'));
const speedContainer = new ButtonContainer(document.querySelector('.speed-container'));
In this way, you don't have to loop through the buttons and add event listeners to each, you simply allow each container to handle it's own button events. I like this approach because we seem to be treating the buttons like two separate groups, and this logically mirrors that.

Related

document.getElementById() not working as intended with multiple ids

I have an issue with document.getElementById(). Basically I have different forms each one with a different id and I'm using a bit of Javascript to replace some classes and add dinamically file name after upload.
That should be really easy, but I don't know why even if the ids are totally unique I get a weird behavior: whatever is the form in which I submit a file javascript will apply changes always on the first of them.
function spinnerLoad(){
document.getElementById('file-name[[${id}]]').textContent = this.files[0].name;
document.getElementById('spinner[[${id}]]').classList.replace('fas', 'spinner-border');
document.getElementById('spinner[[${id}]]').classList.replace('fa-file-upload', 'spinner-border-sm');
document.getElementById('uploadForm[[${id}]]').submit()
}
/*I'm using Bootstrap for my styling rules*/
/*${id} variable is server-side and it's there to make unique each form, I'm using Thymeleaf template engine*/
<form th:id="'uploadForm'+${id}" method="post" enctype="multipart/form-data" th:action="#{/upload/{id} (id=${id})}">
<label for="file-upload" class="btn btn-outline-success">
<span th:id="'spinner'+${id}" class="fas fa-file-upload"></span> <b>Upload file:</b> <i th:id="'file-name'+${id}">No file selected</i>
</label>
<input id="file-upload" type="file" name="multipartFile" accept="application/pdf" style="display: none" th:onchange="spinnerLoad()"/>
</form>
I googled the problem but I didn't manage to find a specific answer to my issue, so that's why I'm here bothering you.
I hope someone can help my figure this out, thank you.
You get a lot of repeating code and that can be hard to maintain. Here I placed the event listener on the the parent <div> to all the buttons. Then I need to test if is a button. And there is no need for an id for each button.
Actually, if you are just replacing a class name you don't even need to do the test (if()), because replace() will only do the replacement when the old value is present. This should be fine:
buttons.addEventListener('click', e => {
e.target.classList.replace('btn-success', 'btn-danger');
});
But here is the full example with the test:
var buttons = document.getElementById('buttons');
buttons.addEventListener('click', e => {
if (e.target.nodeName == 'BUTTON') {
e.target.classList.replace('btn-success', 'btn-danger');
}
});
.btn-success {
background-color: green;
}
.btn-danger {
background-color: red;
}
<div id="buttons">
<button class="btn-success">Button 1</button>
<button class="btn-success">Button 2</button>
<button class="btn-success">Button 3</button>
</div>
You're missing the css that would make this work, but otherwise your example is functional. However, it can be done more simply by working on the buttons as a class instead of individually.
var btns = document.getElementsByClassName("btn");
var addDanger = function(){
this.classList.replace('btn-success', 'btn-danger')
};
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', addDanger, false);
};
.btn {height:20px; width: 50px;}
.btn-success {background-color:green}
.btn-danger {background-color:red}
<button id="btn1" class="btn btn-success"></button>
<button id="btn2" class="btn btn-success"></button>
<button id="btn3" class="btn btn-success"></button>

How would you write this jQuery code that binds a clicked element to functions in JavaScript?

I have being trying to solve a problem of how to bind a clicked element to functions using JavaScript, and after many days finally found a solution here provided by #JerdineSabio. My original problem was asked at Stackoverflow and it had to do with a single JavaScript script handling many buttons and performing speech recognition. The code that binds the clicked button to the functions is written in jQuery and involves using function(e) . The jQuery statement takes 3 parameters, but in JS the equivalent expression only takes two (the event (eg. the clicking) and the function). I've looked up the usual references I depend upon but haven't found a way to write it in Javascript. I have solved the scripting problem; so I just want to find an answer to this question in case I might want to use JS only in the future, as I tend to rely on JS more than jQuery, and I also read about function(e) before and watched a video on Youtube about it, but I still did not quite understand what "e" is and what it does.
The jQuery script below works as it should. The code changes the color of the button that's next to it. Once again, this involves multiple buttons but there is only one script for them all.
I have tried:
document.addEventListener("click", myFunction);
function myFunction(e) {
this.previousElementSibling.setAttribute("style", "background-color: gold") ...
....};
and I've tried a few more things, but I can't make the function work correctly no matter what I try.
The HTML is:
<div class="container">
<button id="first" type="button" class="btn btn-danger"></button>
<button id="first" type="button" class="btn btn-warning"></button>
</div>
The jQuery is:
$(document).ready(function(){
$(document).on("click", "#first", function(e) {
$(this).siblings().css("background-color", "gold");
});
});
You can do in many ways by adding onclick function to the button then target them by id or class name
First by the id (ofc must have unique id) then you gonna put :-
function btnClicked(clicked_btn) {
let btn = document.getElementById(clicked_btn)
btn.style.backgroundColor = 'red'
}
<div class="container">
<button id="first" onClick='btnClicked(this.id)' type="button" class="vtn btn-danger">press1</button>
<button id="second" onClick='btnClicked(this.id)' type="button" class="btn btn-warning">press2</button>
</div>
second by class name :- but you have more than class name so we gonna add split() function to target one of them like so
function btnClicked(clicked_btn) {
let btn = document.querySelector('.' + (clicked_btn.split(' ')[1]))
btn.style.backgroundColor = 'red'
}
<div class="container">
<button id="first" onClick='btnClicked(this.className)' type="button" class="btn btn-danger">press1</button>
<button id="second" onClick='btnClicked(this.className)' type="button" class="btn btn-warning">press2</button>
</div>
You can try this way:
<script>
window.addEventListener('load', () => {
var buttons= document.querySelectorAll('#first');
for (var i = 0; i < buttons.length; i++)
buttons[i].addEventListener("click", myFunction);
});
function myFunction(e) {
siblings(e.target).forEach((element, index) => {
element.setAttribute("style", "background-color: gold")
});
};
function siblings(elem) {
let siblings = [];
if (!elem.parentNode)
return siblings;
let sibling = elem.parentNode.firstElementChild;
do {
if (sibling != elem)
siblings.push(sibling);
} while (sibling = sibling.nextElementSibling);
return siblings;
};
</script>
Basic event delegation requires you to have to figure out what was clicked is the element you are looking for. Using matches() makes that easy.
function delegatedClick(selector, callback) {
function fnc(event) {
if (event.target.matches(selector)) {
callback.call(event.target, event);
}
}
document.addEventListener("click", fnc);
}
delegatedClick(".foo button", function() {
console.log(this.id);
});
.foo button {
color: green;
}
<div class="foo">
<button type="button" id="b1">Yes</button>
</div>
<div class="foo">
<button type="button" id="b2">Yes</button>
</div>
<div class="bar">
<button type="button" id="b3">No</button>
</div>
<div class="foo">
<button type="button" id="b4">Yes</button>
</div>
Now toggling the siblings
var wrapper = document.querySelector(".foo");
var buttons = wrapper.querySelectorAll("button")
wrapper.addEventListener("click", function (event) {
var clickedButton = event.target.closest("button");
clickedButton.classList.remove("goldbg");
buttons.forEach(function (btn) {
if (btn !== clickedButton) {
btn.classList.add("goldbg");
}
});
});
.foo button.goldbg {
background-color: gold;
}
<div class="foo">
<button type="button" id="b1">1</button>
<button type="button" id="b2">2</button>
<button type="button" id="b3">3</button>
<button type="button" id="b4">4</button>
</div>

How to undo "this.onclick=null"? How to restore javascript onclick functionality to a div?

I am displaying a div with a default background color. When I click it once, it changes color. A second click does nothing, because the div has this code: "this.onclick=null". That much works well.
However, after I click the div once, I want to click a button to restore its onclick functionality. But the button I have created for that purpose doesn't work, because I don't know what javascript code to use. Does anyone know?
As you can see, I'm working with Bootstrap 3. (Please note that I am a beginner with only basic knowledge of php and js.) Here is my code:
HTML for the div:
<div class="row">
<div class="col-sm-12">
<div id="testDiv" style="background-color: #0000FF" onclick="colorClick(id); this.onclick=null;">
Clicking this div once changes the color.
</div>
</div>
</div>
JAVASCRIPT for the div:
function colorClick(id){
var randomColor = '#'+Math.floor(Math.random()*16777215).toString(16);  
document.getElementById(id).style.backgroundColor=randomColor;
/* The random color generator is from stackoverflow.com/questions/1484506/random-color-generator */
}
HTML for the button:
<div class="row">
<div class="col-sm-12">
    <button type='button' class='btn btn-default btn-lg' onclick=reEnableOnclick()>
This button should re-enable the onclick functionality, but it does not
</button>
</div>
</div>
JAVASCRIPT for the button:
function reEnableOnclick(){
document.getElementById("testDiv").onclick=click;  /* This is only pseudo-code. What real code should I put here? */
}
(Both js functions are in a separate .js document)
EDIT #2: This needs to work for about 100 divs on the same page.
Since you need to apply this functionality to 100s of divs, try keeping track of just the locked states rather than all the divs states at all times. Assuming each div id is unique you could try:
Javascript:
let locked = [];
function lockCheckRun(id) {
let divID = document.getElementById(id);
if (!locked.includes(divID) {
colorClick(id);
locked.push(divID);
}
}
function lockReset() {
locked.length = 0;
}
Explanation:
create an empty array to store locked element ids. We'll call this array 'locked'.
next, create a function which checks to see if the locked array includes the current unique element id. if it does not, then we will run your 'colorClick' function. once that completes, we will add the current element id to the 'locked' array via .push()
when you want to reset your locked element array, run the 'lockReset' function.
Optional Polyfill for comptability with older browsers:
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
}
if (start === undefined) { start = 0; }
return this.indexOf(search, start) !== -1;
};
}
Assign the original onclick code.
function reEnableOnclick() {
document.getElementById("testDiv").onclick = "colorClick(id); this.onclick=null;";
}
You can have a pseudo variable inside your javascript code that serves the purpose
In this case you can write
HTML Code
<div class="row">
<div class="col-sm-12">
<div id="testDiv" style="background-color: #0000FF" onclick="colorClick(id)">
Clicking this div once changes the color.
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<button type='button' class='btn btn-default btn-lg' onclick=reEnableOnclick()>
This button should re-enable the onclick functionality, but it does not
</button>
</div>
</div>
Javascript Code
var divDisabled = false;
function colorClick(id){
if(!divDisabled) {
var randomColor = '#'+Math.floor(Math.random()*16777215).toString(16);
document.getElementById(id).style.backgroundColor=randomColor;
divDisabled = true;
/* The random color generator is from stackoverflow.com/questions/1484506/random-
color-generator */
}
}
function reEnableOnclick(){
divDisabled = false;
}
So now, You will set divDisabled for false by default so the first click of div will work as you want & following clicks on div won't as it is currently,
But As soon as you hit the button click it enables the click on div with setting buttonDisable to false, so it will again work once on div, as you want
Don't remove the click event, just check a flag to see if the behaviour is enabled or not. E.G:
var clickEnabled = true;
function colorClick(id) {
if (clickEnabled) {
var randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
document.getElementById(id).style.backgroundColor = randomColor;
clickEnabled = false;
}
}
function reEnableOnclick() {
clickEnabled = true;
}
<div class="row">
<div class="col-sm-12">
<div id="testDiv" style="background-color: #0000FF" onclick="colorClick(id);">
Clicking this div once changes the color.
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<button type='button' class='btn btn-default btn-lg' onclick=reEnableOnclick()>
This button will re-enable the onclick functionality
</button>
</div>
</div>
You could define the function to run onclick outside of the assignment and use it to set and reset the click handler...
function functionThatRunsWhenButtonClicked() {
// do stuff
}
then in the places you need to assign it, assign it..
<div class="row">
<div class="col-sm-12">
<div id="testDiv" style="background-color: #0000FF" onclick="functionThatRunsWhenButtonClicked(); this.onclick=null;">
Clicking this div once changes the color.
</div>
</div>
</div>
function reEnableOnclick(){
document.getElementById("testDiv").onclick=functionThatRunsWhenButtonClicked; /* This
is only pseudo-code. What real code should I put here? */
}
You can do the same thing with colorClick(id)

Changing the Style of a HTML-Button within a JavaScript Function

I am trying to change the style class of a button when clicking it but it is not working how it should.
I need the style/icon change inside a JS function because I am doing some more stuff when the buttons gets clicked.
function generate() {
//// Some stuff
document.getElementById('btn1').class = 'play icon fa fa-stop';
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<div class='container20'>
<button class='play icon fa fa-play-circle-o' type='button' onclick='generate()' id="btn1">Button</button>
</div>
class not exist, there is className, so replace it by className
In onclick there is function so, you need to add () in it so, replace generate to generate().
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<div class='container20'>
<button class = 'play icon fa fa-play-circle-o' type = 'button' onclick = 'generate()' id = "btn1">Button</button>
</div>
<div class='container80'>
<script>
function generate() {
//// Some stuff
document.getElementById('btn1').className = 'play icon fa fa-stop';
}
</script>
</div>
Use className and not class
document.getElementById('btn1').className = 'play icon fa-stop';
Try the following:
Below code will add the 3 classes to any existing classes already applied to the button
document.getElementById('btn1').classList.add('play', 'icon', 'fa-stop');
Below code will replace any existing classes already added to the button with play, icon and fa-stop
document.getElementById('btn1').className = 'play icon fa-stop';
Rather than setting className, and having to preserve the play icon parts of the className text, you can use classList to remove and add the necessary classes
function generate() {
const b = document.getElementById('btn1')
b.classList.remove('fa-play-circle-o`);
b.classList.add('fa-stop');
}
Yes, it's more lines, but it's more flexible too. For example you could write a method that takes the old class and the new class and call it from anywhere.
function changeClass(element, old, new) {
element.classList.remove(old);
element.classList.add(new);
}
Also, since you are running in an event handler, you don't have to getElementById('btn1') because the event.target is the button that was clicked.
This (below) attaches an event handler, rather than using an inline onclick=, and I've added background color just so the effect of clicking the button can be seen.
document.getElementById('btn1')
.addEventListener('click', function(e) {
e.target.classList.remove('fa-play-circle-o');
e.target.classList.add('fa-stop');
});
button {
font-weight: bold;
}
button.fa-play-circle-o {
background-color: lightsalmon;
}
button.fa-stop {
background-color: lightgreen;
}
<div class="container20">
<button id="btn1" type="button"
class="play icon fa-play-circle-o">Button</button>
</div>

Hide and show button

I have a task in school and I think Javascript is really hard. Now in the beginning I need to google everything and I only find solutions in different libraries.
In this case I need to use Vanilla JS. When I click on logout button it need to toggle and show the login button.
In the task I cant change the HTML only add JS.
// student
<button class="signin-btn is-hidden">Log in </button>
<button class="signout-btn">Log out </button>
You can add click event listeners to both of the buttons to change its own display to none and display the other button.
var login = document.querySelector('.signin-btn'),
logout = document.querySelector('.signout-btn');
login.addEventListener('click', function(e){
this.classList.add('is-hidden');//adds is-hidden class
logout.classList.remove('is-hidden');//removes is-hidden class
});
logout.addEventListener('click', function(e){
this.classList.add('is-hidden');
login.classList.remove('is-hidden');
});
.is-hidden{
display: none;
}
<button class="signin-btn is-hidden">Log in </button>
<button class="signout-btn">Log out </button>
here's a extremely basic way to do it, I would recommend using more advanced techniques later
function change() {
document.getElementById('but').style.display = "none"
document.getElementById('butother').style.display = "block"
}
function changeother() {
document.getElementById('butother').style.display = "none"
document.getElementById('but').style.display = "block"
}
<html>
<body>
<button id="but" onclick='change()'>log in </button>
<button id="butother" onclick='changeother()'>log out </button>
</body>
</html>

Categories