1

so when i click on the <p> tag of the home class, i want it to change color to green but it doesn't work and idk why. the click registers fine (as the console.log("test") displays just fine) but the rest of the function to change the color won't work. here's my css, html and js code (the js code is contained in the HTML, so it's not an external file or anything):

.greyRect {

  height:150px;
  width:1350px;

  background-color: #D3D3D3;


}
h1 {
  text-align: center;
}
h2 {
    text-align: center;
}
.home {



box-sizing: border-box;
width:80px;
height:35px;

line-height: 2;
  position: relative;
left:350;
  color:white;

}
.casinocraps {
  background-color: grey;

box-sizing: border-box;
width:120px;
height:35px;
text-align: center;
line-height: 2;
  position: relative;
left:460;
bottom:50;
  color:white;
}
.tictactoe {
  background-color: grey;
  box-sizing: border-box;
  width:90px;
  height:35px;
  text-align: center;
line-height: 2;
    position: relative;
left:600;
bottom:100;
    color:white;
}
.bingo {
  background-color: grey;
  box-sizing: border-box;
  width:80px;
  height:35px;
  text-align: center;
  line-height: 2;
    position: relative;
  left:700;
  bottom:150;
    color:white;
}
.concentration {
  background-color: grey;
  box-sizing: border-box;
  width:100px;
  height:35px;
  text-align: center;
  line-height: 2;
    position: relative;
  left:800;
  bottom:200;
    color:white;
}
footer {
  text-align: center;
    line-height: 4;
      position: relative;
  top:125;
  right:15;
  height:70px;
  width:1365px;

  background-color: #D3D3D3;
}
.border {
  height: 50px;
  width: 100px;
  border: 4px solid green;
  background-color: #555;
  position: relative;
  top:20;
  left:100;
}
.rectangle {
  height: 50px;
  width: 100px;
  background-color: #555;
  position: relative;
  top:50;
  left:100;
}
<html>
  <head>

    <meta charset="utf-8">

    <title></title>
      <link rel="stylesheet" type="text/css" href="cssForAss4.css">
  </head>
  <body>

<header class="greyRect" >
<h1>Assignment 1</h1>

<h2>Home Page</h2>
<nav>
<p class="home" onclick="selectHome()">
Home
</p>
<p class="casinocraps">

<b>Casino Craps</b>
</p>
<p class="tictactoe">

<b>Tic-Tac-Toe</b>
</p>
<p class="bingo">

<b>Bingo</b>
</p>
<p class="concentration">

<b>Concentration</b>
</p>
</nav>
<div class="border">
</div>
<footer >Footer</footer>

</header>

<script>
function selectHome() {
  console.log("test");

document.getElementsByClassName("home").style += "background-color:green;";


}
</script>
  </body>
</html>
Scott Marcus
  • 57,085
  • 6
  • 34
  • 54
Clock
  • 25
  • 1

3 Answers3

21

Others have suggested .getElementsByClassName("home")[0], which is a terrible idea.

First, .getElementsByClassName() returns a node list of all the matching elements. If you are only interested in the first one, it makes no sense to find that one and then keep scanning for more matches and then discard all but the first one found, which is what this code does.

Second, .getElementsByClassName() returns a "live" node list. This means that every time you interact with the list, the entire DOM is searched again for matches, ensuring that you have the most up to date set in your list. This can be useful in applications where nodes are being added and removed dynamically, but those use cases aren't as common.

FYI: .getElementsByTagName(), .getElementsByName(), and node.childNodes also return live node lists.

All of these previously mentioned methods date back to the earliest days of the DOM API, when it was still the "wild west" days of web development. They are all over two decades old and have much better alternatives today (i.e. .querySelector(), .querySelectorAll(), .closest()).

When it's not necessary to keep an up to date list, .querySelectorAll() is the way to go. And frankly, even if you do need an updated node list, you're still better off with .querySelectorAll() and just run it again manually at the point where you need an updated list.

Here's a good page that discusses this and here's what it has to say:

How to Think About Live Object?

Live object is not intuitive. You can think of it as delayed evaluation or lazy evaluation. Method or property of live object is re-computed when their result is accessed.


But, in this case, we don't even want a node list, we just want a single node. The correct solution would be:

document.querySelector(".home");

.querySelector() scans the document for the first element that matches the supplied selector and, if found, returns a reference to that single node. Otherwise, it returns undefined.

Scott Marcus
  • 57,085
  • 6
  • 34
  • 54
  • 2
    Fantastic explanation. Absolutely should have suggested querySelector in my answer. Thanks for the correction. – Thomas Prince Mar 02 '19 at 03:48
  • OK, this clears up a comment I made earlier, but what has me confused is why cloning the live collection with `Array.from()` or `[...document.getElementsByClassName("test")]` is still slower than `querySelectorAll()`. I guess it has to add the live-collection supporting code and then immediately tear it down. – zero298 Jun 04 '19 at 21:24
  • @zero298 Why would you think that adding the extra step of creating an Array from the elements found from a DOM query and placed in a node list would be faster than just performing the DOM query and creating a node list? – Scott Marcus Jun 04 '19 at 21:30
  • I was thinking the live-collection code might be less overhead than the need to look for every DOM element and check if it matches the query vs every element and just checking its class since querySelectorAll can search with more complex searches than just a class name compare. – zero298 Jun 04 '19 at 21:33
  • `.querySelector()` and `.querySelectorAll()` are much better candidates for optimization than `.getElementsByClassName()`. – Scott Marcus Jun 04 '19 at 21:38
  • 1
    The linked test is invalid as the static node list is created once with length zero then never updated, so accessing the length property returns the wrong value. Adding `document.querySelectorAll('.test')` to each loop makes it somewhat **slower** than *getElementsByTagName*. You assume implementation details in "the document must be scanned", you don't know that. The collection might have an index that is updated as modifications occur, potentially making it faster for large collections than *querySelectorAll*, which likely depends on an index of all nodes in the document. – RobG May 13 '20 at 22:28
  • @RobG There was an error in the test (forgot the `.` before `test` in `querySelectorAll()`), but the point of the test is to show that simply interacting with a Live node list causes the list to have to be updated, which incurs a performance hit. Of course adding `querySelectorAll()` to the loop would cause that test to be slower, but that's not what the test is trying to show. It's not a head to head between `getElementsByClassName()` and `querySelectorAll()` it's comparing the efficiency of a live node list against a static one. – Scott Marcus May 14 '20 at 12:46
  • @RobG As for the implementation details, the live node list is updated at the time you access it, not as the DOM is mutated. You're right that the details of how the update is implemented is not dictated by the standard, but it doesn't change the fact that merely interacting with the live node list causes an update - - that's the entire point and shown quite clearly by the test (which I have updated). – Scott Marcus May 14 '20 at 12:50
  • getElementsByClassName returns a list from a hash table and does no "scanning". Run this in dev console on this page, querySelector's linear lookup gets shredded in all cases: `start = (new Date).getTime(); for (let i = 0; i < 1000000; i++) { document.querySelector(".comment-copy"); } console.log((new Date).getTime() - start);` `start = (new Date).getTime(); for (let i = 0; i < 1000000; i++) { document.getElementsByClassName("comment-copy")[0]; } console.log((new Date).getTime() - start);` – Sasha Kondrashov May 06 '21 at 07:05
1

Do it like this:

function selectHome() {
  document.getElementsByClassName("home")[0].style.backgroundColor = "green";
}
<p class="home" onclick="selectHome()">
  Home
</p>
Sasha Kondrashov
  • 2,214
  • 1
  • 14
  • 22
Anurag Srivastava
  • 12,230
  • 3
  • 21
  • 36
1

.style is actually a js object with keys corresponding to css properties.

As Adarsh said

document.getElementsByClassName("home")[0].style.backgroundColor = "green"

Edit - Don't do this. As Scott Marcus explains, this is pretty bad. Definitely should use querySelector('.home') to get the element.

Generally, if a property has a hyphen like background-color, you convert it to camel case ie backroundColor

Check out MDN - HTMLElement.style

Thomas Prince
  • 332
  • 1
  • 5
  • `document.getElementsByClassName("home")` returns an `array` – Anurag Srivastava Mar 01 '19 at 20:56
  • @AnuragSrivastava Nice catch! Edited my answer. Should probably do some defensive checking to make sure the element exists before trying to access .style, but left that out to keep things simple – Thomas Prince Mar 01 '19 at 20:59
  • 1
    @AnuragSrivastava `.getElementsByClassName()` returns a "node list", not an Array. – Scott Marcus Mar 01 '19 at 21:00
  • @ScottMarcus My bad! – Anurag Srivastava Mar 01 '19 at 21:02
  • Also, `document.getElementsByClassName("home")[0]` makes no sense. Why scan the whole DOM to find all the matches, just to throw them all away and only hold the first. `.getElementsByClassName()` returns a "live" node list which hurts performance in the first place, but not using the node list makes matters worse. Just use `.querySelector()`. – Scott Marcus Mar 01 '19 at 21:06
  • 1
    @ScottMarcus A bit off the thread, but "_a live node list ... hurts performance_" ..? Any evidence of that somewhere? AFAIK `querySelector(All)` are slow by nature, since they're performing a string search, whereas `gEBCN` searches from the DOM ... – Teemu Mar 01 '19 at 21:13
  • @Teemu Because of the way live node lists work. Every single time you reference the variable that holds a reference to the node list, the entire dom is scanned again for matches. This is how the list is guaranteed to return the most up to date set of results. – Scott Marcus Mar 01 '19 at 21:15
  • @ScottMarcus Ah, how live collections actually work is beyond my knowledge (if you just had a link?). But in this particular case the collection is retrieved only once, and then thrown away. – Teemu Mar 01 '19 at 21:19
  • @ScottMarcus Actually, I've had an impression that live collections are updated every time the page is re-calculated, but I might be wrong with this ... – Teemu Mar 01 '19 at 21:27
  • 1
    @Teemu It's often worded that way to explain the *results* of having a live node list, but in actuality, they function differently. – Scott Marcus Mar 01 '19 at 21:32
  • @Teemu Check out this [JS Perf](https://jsperf.com/live-node-list-vs-static-node-list). – Scott Marcus Mar 01 '19 at 21:48
  • 1
    @ScottMarcus Hmm ... based on your test, it looks like the live collections are useless, the same result would be achieved by simply calling a `gEBXN` every time you need a collection or a member of it. – Teemu Mar 01 '19 at 22:01
  • 1
    @Teemu I agree. But, if you understand the history of how the DOM API evolved, it makes sense. `.querySelectorAll()` didn't come along until many years after the "live" node list methods were in place. Those are antiquated now and shouldn't really be used, except for extreme use cases. This is why you'll find me calling out the use of this all over Stack Overflow. ;) – Scott Marcus Mar 01 '19 at 22:07
  • @ThomasPrince I would keep your answer without the disclaimer. Scott's information is wrong; getElementsByClassName does not perform a search and therefore is always faster, in pretty much every conceivable scenario, than querySelector. See my comment on his answer for a benchmark. It achieves this by using a hash table that already contains lists of all elements belonging to each class, this table is updated whenever an element is added to the DOM, and so finding the first element of any of those lists is an extremely fast operation. querySelector is too general to use this optimization. – Sasha Kondrashov May 06 '21 at 07:12