Tobiloba Olugbemi
Tobiloba Olugbemi

Reputation: 1

Button styling bug

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>

Upvotes: 0

Views: 597

Answers (2)

djs
djs

Reputation: 4065

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.

Upvotes: 1

ctaleck
ctaleck

Reputation: 1665

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>

Upvotes: 4

Related Questions