4

I am trying to use JavaScript to change the background color of an element after being selected, and also to make sure that only one element at a time has the particular background color. Once the user selects on a different element I would like the previous element that was selected to be replaced by a different background color. Currently I am only able to toggle individual elements by selecting on EACH element. I need to be able to select on an element and apply the new background color, then have JavaScript change the background color of the previously active element to a different color (one less click).

What I am trying to do is very similar to modern navbars or list items where only one element at a time is “active” and has a background color that is different than the other elements in the same div, row, etc.

Notes about my work I am utilizing bootstrap and have no desire to use jQuery for this particular project.

CSS:

<!DOCTYPE html>
<html lang="en">
    <head>
        <style>
            h4 {
                border: 1px solid black;
                border-radius: 8px;
                padding: 10px 2px 10px 2px;
                margin: 20px 20px 0px 20px;
                background-color: #F0F0F0;
                border-color: #F8F8F8;
                color: #505050;
                cursor: pointer;
            }

            .active {
                background-color: #99E6FF;
            }
        </style>
    </head>
</html>

HTML:

<div id="pTwoRowOne">
    <div class="row">
        <div class="col-md-4 row row-centered">
            <h4 id="techBio" class="test">Biology</h4>
        </div>
        <div class="col-md-4 row row-centered">
            <h4 id="techCart" class="test">Cartography</h4>
        </div>
        <div class="col-md-4 row row-centered">
            <h4 id="techChem" class="test">Chemistry</h4>
        </div>
    </div>
</div>

JavaScript:

document.getElementById("techBio").onclick=function() {
    document.getElementById("techBio").classList.toggle('active');
}

document.getElementById("techCart").onclick=function() {
    document.getElementById("techCart").classList.toggle('active');
}

document.getElementById("techChem").onclick=function() {
    document.getElementById("techChem").classList.toggle('active');
}

An example can be seen here: http://jsbin.com/fugogarove/1/edit?html,css,js,output

If clarification is needed let me know.

4 Answers 4

4

Yup, pretty straightforward.

Assumptions

  1. You're not trying to support IE8, since you're using classList
  2. You're okay with housing your elements as variables as opposed to repeatedly querying the DOM.

Example

JSBin

Code

I rewrote your JavaScript to make it a little bit cleaner and to DRY it up a bit:

var techs = [].slice.call(document.querySelectorAll('#pTwoRowOne h4'));

function set_active(event) {
  techs.forEach(function(tech){
    if (event.target == tech) { return; }
    tech.classList.remove('active');
  });
  event.target.classList.toggle('active');
}

techs.forEach(function(item) {
  item.addEventListener('click', set_active);
});

Some explanation

[].slice.call(document.querySelectorAll('#pTwoRowOne h4')); – We're using this to change the output from a NodeList to an Array. This allows us to use forEach later. querySelectorAll returns a NodeList that contains all elements matching the CSS selector. You can probably replace that with a better CSS selector depending on your environment.

addEventListener is a much nicer way than the iterative add via onclick += to bind an event listener. It's also the recommended way (as far as I know) in ECMA5 and later.

By setting the element queries as variables, you'll be able to keep the reference in memory instead of polling the DOM every time to alter elements. That'll make your JavaScript marginally faster, and it's again just a nicer, cleaner version of the code which it produces.

updates

I reworked the JS to make more sense.

Sign up to request clarification or add additional context in comments.

1 Comment

Note classList can be polyfilled for IE8.
3

Assuming you only ever have one active element, you can find it using document.querySelector() - if you can have multiples you can use document.querySelectorAll() and iterate through them.

Simple case:

function activate(event) {
  var active=document.querySelector('.active');

  // activate the clicked element (even if it was already active)
  event.target.classList.add('active');

  // deactivate the previously-active element (even if it was the clicked one => toggle)
  if (active) active.classList.remove('active');
}

document.getElementById("techBio").addEventListener("click",activate);
document.getElementById("techCart").addEventListener("click",activate);
document.getElementById("techChem").addEventListener("click",activate);
h4 {
   border: 1px solid black;
   border-radius: 8px;
   padding: 10px 2px 10px 2px;
   margin: 20px 20px 0px 20px;
   background-color: #F0F0F0;
   border-color: #F8F8F8;
   color: #505050;
   cursor: pointer;
}
	
.active {
   background-color: #99E6FF;
}
<div id="pTwoRowOne">
  <div class="row">
    <div class="col-md-4 row row-centered">
      <h4 id="techBio" class="test">Biology</h4>
    </div>
    <div class="col-md-4 row row-centered">
      <h4 id="techCart" class="test">Cartography</h4>
    </div>
    <div class="col-md-4 row row-centered">
      <h4 id="techChem" class="test">Chemistry</h4>
    </div>
  </div>
</div>

3 Comments

I like your JavaScript better. :(
Nice to have options, and explanations
Well, it does now at least. Thanks for the inspiration.
1

Another similar yet simpler way to do it: jsBin ;)

var H4 = document.getElementsByClassName("test"), act;

[].forEach.call(H4, function(el){
    el.addEventListener("click", function(){
       if(act) act.classList.remove("active");
       return (this.classList.toggle("active"), act=this);
    });
});

2 Comments

These answers all work very well so thank you for that @Josh and others. My only remaining question (as I am not a JS expert) what are the advantages or disadvantages to using any single method mentioned here over another. Be as brief of thorough as you like as this is a fairly open ended question especially without knowing more about my project.
@Hambone Array.forEach.call is introduced in ES5 (ECMAScript 5) so it might need polyfills for older browsers. Without a polyfill you should simply loop all your elements using a standard for(var i=0; i<H4.length; i++){/* operate here over H4[i] */}. classList is also an ES5 feature. So basically we all purposely fail to add support to oldies :) which should be fine since we're in mid 2015. Otherwise use the code that's more readable and easier to understand.
1

You can do something like this:

[].slice.call(document.querySelectorAll(".test")).forEach(function(element) {
  element.addEventListener('click', function(event) {
    if (activeElement = document.querySelector(".test.active")) {
      activeElement.classList.remove("active");
    };
    event.target.classList.add('active');
  });
});

Basically, first we remove the active class from the active element, then we add it to the target.

JSBin

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.