0

I am back again with another JavaScript DOM question. Avoiding jQuery.

I am trying to select all elements of the same class in an HTML table and highlight them as blue on mouseover. I would like to highlight all the cells that have the same classname as the hovered element. So far, I have come up with two solutions to this problem and neither are working.

Solution One:

var cellElements = document.getElementsByTagName('td');
for (var i = 0; i < cellElements.length; i++){
  cellElements[i].addEventListener('mouseover', function(e){
    var hoveredElement = e.target;
    if (cellElements[i].className === hoveredElement.className){
      cellElements.style.color = "blue";
    }
    else {
      cellElements.style.color = "black";
    }
  })
}

or Solution Two:

var cellElements = document.getElementsByTagName('td');
for (var i = 0; i < cellElements.length; i++){
  cellElements[i].addEventListener('mouseover', function(e){
    var hoveredElement = e.target;
    document.getElementsByClassName(hoveredElement.className).style.color="blue";
  })
}

HTML Snippet:

<tr class="row-c tr-selected">
  <td><input type="checkbox"></input></td>
  <td class="column-a">Specification 3</td>
  <td class="column-b">Specification 3</td>
  <td class="column-c">Specification 3</td>
</tr>
<tr class="row-d tr-selected">
  <td><input type="checkbox"></input></td>
  <td class="column-a">Specification 3</td>
  <td class="column-b">Specification 3</td>
  <td class="column-c">Specification 3</td>
</tr>

I would like to try and figure out how this is done without jQuery.

HannahBanana
  • 101
  • 1
  • 2
  • 8
  • You want to highlight all at once or just the hovered one? – kind user Mar 14 '17 at 23:44
  • @Kinduser I would like to highlight all the cells that have the same classname as the hovered element. Thanks for the question. I am going to edit this post to clarify that. – HannahBanana Mar 14 '17 at 23:47
  • HTML code would be also helpful. – kind user Mar 14 '17 at 23:48
  • Title is misleading. What do you want to compare ? – tinker Mar 14 '17 at 23:49
  • any reason not to use css `:hover` instead? I know you want to "figure out how this is done without jQuery" ... CSS is a perfectly valid way to do this without jQuery :p – Jaromanda X Mar 14 '17 at 23:52
  • @JaromandaX Is there a CSS selector that will highlight all elements that have the same class, instead of the individual element? – HannahBanana Mar 14 '17 at 23:54
  • @Kinduser I am going to include the HTML now. – HannahBanana Mar 14 '17 at 23:55
  • @sking I updated the title. – HannahBanana Mar 14 '17 at 23:55
  • @HannahBanana There's no a CSS selector that will highlight `every` element with given `class`, although there is something like `:hover` in CSS but it will highlight **only** the hovered element, not every element with the same class. – kind user Mar 14 '17 at 23:57
  • I misunderstood the requirement – Jaromanda X Mar 14 '17 at 23:59
  • @kinduser Yes, aware of this. Wanted to highlight all elements however, so JavaScript seemed to be the way to go. – HannahBanana Mar 14 '17 at 23:59
  • @HannahBanana I have provided you a working solution. – kind user Mar 15 '17 at 00:00
  • Solution two would never work because you can not do that to an HTML collection. – epascarello Mar 15 '17 at 00:13
  • You can do this by modifying the CSS rule, that way you don't have to loop over all the elements with the same class name. I'm just too busy (i.e. lazy) to post a solution. ;-) See [*modify a css rule object with javascript*](http://stackoverflow.com/questions/13528512/modify-a-css-rule-object-with-javascript). – RobG Mar 15 '17 at 00:45

2 Answers2

0

You have binded the event listeners correctly, but then you have just to catch the elements with the same class as the hovered element and drop it inside variable. Then, using Array#forEach, iterate over these elements to change their style.

var cellElements = document.getElementsByTagName('td');
for (var i = 0; i < cellElements.length; i++) {

  cellElements[i].addEventListener('mouseover', function(e) {
    var elems = document.getElementsByClassName(this.className);
    Array.from(elems).forEach(v => v.style.color = 'red');
  })
  
  cellElements[i].addEventListener('mouseleave', function(e) {
    var elems = document.getElementsByClassName(this.className);
    Array.from(elems).forEach(v => v.style.color = 'black');
  })
  
}
<table>
<tr class="row-c tr-selected">
  <td><input type="checkbox"></td>
  <td class="column-a">Specification 3</td>
  <td class="column-b">Specification 3</td>
  <td class="column-c">Specification 3</td>
</tr>
<tr class="row-d tr-selected">
  <td><input type="checkbox"></td>
  <td class="column-a">Specification 3</td>
  <td class="column-b">Specification 3</td>
  <td class="column-c">Specification 3</td>
</tr>
</table>
kind user
  • 40,029
  • 7
  • 67
  • 77
  • Did not know a nodelist would accept array methods! Interesting. Array.from() looks quite useful, thank you. – HannahBanana Mar 15 '17 at 00:12
  • @HannahBanana It is a nodelist, but that's why I've used `Array.from()` function. It changes a nodelist (array-like object) into an array. Then, you are able to iterate over it! (: – kind user Mar 15 '17 at 00:13
  • Array.from is good - except it doesn't work in Internet Exploder - see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Polyfill for a polyfill – Jaromanda X Mar 15 '17 at 00:17
0

Your second example is actually pretty close, there are just a couple of tweaks you need to make.

First, be careful never to declare a function inside a for loop; doing so can have some weird side effects. You can easily move the declaration out like this:

var cellElements = document.getElementsByTagName('td');
function colorSimilar (e) {
  var hoveredElement = e.target;
  document.getElementsByClassName(hoveredElement.className).style.color="blue";
}
for (var i = 0; i < cellElements.length; i++){
  cellElements[i].addEventListener('mouseover', colorSimilar)
}

Second, you're trying to modify the property style on the result of a call to getElementsByClassName, but that result is not a single element but rather an array-like object of all child elements which have all of the given class names. So what you want to do is iterate over that array-like object; one simple way to do that is with a standard for loop:

function colorSimilar (e) {
  var hoveredElement = e.target;
  var similarElements = document.getElementsByClassName(hoveredElement.className)
  for (var i = 0; i < similarElements.length; i++) {
    similarElements[i].style.color = "blue";
  }
}

(another way would be to cast the HTMLCollection to an array using Array.from if your browser supports it, or by using forEach generically with Array.prototype.forEach.call)

Third, you might notice then that your elements actually stay blue, even after mousing over a different group of similar classnames. This is because you're never resetting the colors! One easy way to do so is to instead iterate over all candidates, and switch the color based on the classname:

function colorSimilar(e) {
  var hoveredElement = e.target;
  for (var i = 0; i < cellElements.length; i++) {
    if (cellElements[i].className == hoveredElement.className) {
      cellElements[i].style.color = 'blue';
    } else {
      cellElements[i].style.color = null;
    }
  }
}

Your final result should look something like this:

(note that I'm using backgroundColor rather than color, simply because it makes for a more fun example)

var container = document.getElementById('container');
var cellElements = container.getElementsByTagName('td');

function colorSimilar(event) {
  var className = event.target.className;
  for (var i = 0; i < cellElements.length; i++) {
    if (cellElements[i].className == className) {
      cellElements[i].style.backgroundColor = 'blue';
    } else {
      cellElements[i].style.backgroundColor = null;
    }
  }
}

for (var i = 0; i < cellElements.length; i++) {
  cellElements[i].addEventListener('mouseover', colorSimilar)
}
td {
  height: 40px;
  width: 40px;
  border: 1px solid black;
}
<table id="container">
  <tr>
    <td class="one" />
    <td class="two" />
    <td class="three" />
  </tr>
  <tr>
    <td class="three" />
    <td class="one" />
    <td class="two" />
  </tr>
  <tr>
    <td class="two" />
    <td class="three" />
    <td class="one" />
  </tr>
</table>
Hamms
  • 5,016
  • 21
  • 28