Skip to content

Error or bug when switching CSS classes by JavaScript when dragging with mouse [closed]

Ok, here a simple code, which changes td class when you click on them:

const btn = document.getElementsByTagName("input")[0];
btn.addEventListener("click", function(event){
  
  let cells = document.getElementsByTagName("td");
  
  for (let i=0; i<cells.length; i++) {
    cells[i].classList = ""
  };
  
  CounterCells();
})





const tabl = document.getElementsByTagName("table")[0];
tabl.addEventListener("click", function(event){
  
  if (event.target.classList == ""){
    event.target.classList.add("green");
    console.log("Nothing to green");
    
  } else if (event.target.classList.contains("white")){
    event.target.classList.replace("white", "green");
    console.log("White to green");
    
  } else if (event.target.classList.contains("green")){
    event.target.classList.replace("green", "red");
    console.log("Green to red");
    
  } else if (event.target.classList.contains("red")) {
    event.target.classList.replace("red", "white");
    console.log("Red to white");
  }
  
  CounterCells();

})







function CounterCells() {
  let cells = document.getElementsByTagName("td");
  
  let countWhites = 0;
  let countGreens = 0;
  let countReds = 0;
  
  for (let i=0; i<cells.length; i++) {
    if (cells[i].classList == "") {
      countWhites++
    }
    if (cells[i].classList.contains("white")) {
      countWhites++
    }
    if (cells[i].classList.contains("green")) {
      countGreens++
    }
    if (cells[i].classList.contains("red")) {
      countReds++
    }
  }
  
  const p = document.getElementById("demo");
  p.innerHTML = "Whites: "+countWhites+"<br> Greens: "+countGreens+"<br> Reds: "+countReds;
}
table {
  border-collapse: collapse;
}

td {
  border: 1px solid grey;
  width: 2rem;
  height: 2rem;
  text-align: center;
  cursor: pointer;
  user-select: none;
}

.green {
  background-color: green;
}

.red {
  background-color: red;
}

.white {
  background-color: white;
}
<input type="button" value="Reset">

<br>
<br>

<table>
  <tr>
    <td>1</td>
    <td>2</td>
    <td>3</td>
  </tr>
  <tr>
    <td>4</td>
    <td>5</td>
    <td>6</td>
  </tr>
  <tr>
    <td>7</td>
    <td>8</td>
    <td>9</td>
  </tr>
</table>

<p id="demo"></p>

If you click it — anything works fine, but now try to click on cell number one and drag mouse over cell number three and then release mouse button — class will be assigned to tr not to last td number 3.

Why is that?

https://jsfiddle.net/foxnadir/Ls6p7j1z/3/

Answer

The problem in your code was that you were attaching the click event to the table element, when only one td can be clicked at a time.

I also changed the event from click to mousedown so that when the user drags from 1 to 3, 1 changes color, but if this behavior is not what you wanted, you can change it back.

Here is the working code:

const btn = document.getElementsByTagName("input")[0];
btn.addEventListener("click", function(event) {

  let cells = document.getElementsByTagName("td");

  for (let i = 0; i < cells.length; i++) {
    cells[i].classList = ""
  };

  CounterCells();
})

let cells = document.getElementsByTagName("td");

for (i = 0; i < cells.length; i++) {
  cells[i].addEventListener("mousedown", function(event) {

    if (event.target.classList == "") {
      event.target.classList.add("green");
      console.log("Nothing to green");

    } else if (event.target.classList.contains("white")) {
      event.target.classList.replace("white", "green");
      console.log("White to green");

    } else if (event.target.classList.contains("green")) {
      event.target.classList.replace("green", "red");
      console.log("Green to red");

    } else if (event.target.classList.contains("red")) {
      event.target.classList.replace("red", "white");
      console.log("Red to white");
    }

    CounterCells();

  })
}

function CounterCells() {

  let countWhites = 0;
  let countGreens = 0;
  let countReds = 0;

  for (let i = 0; i < cells.length; i++) {
    if (cells[i].classList == "") {
      countWhites++
    }
    if (cells[i].classList.contains("white")) {
      countWhites++
    }
    if (cells[i].classList.contains("green")) {
      countGreens++
    }
    if (cells[i].classList.contains("red")) {
      countReds++
    }
  }

  const p = document.getElementById("demo");
  p.innerHTML = "Whites: " + countWhites + "<br> Greens: " + countGreens + "<br> Reds: " + countReds;
}
table {
  border-collapse: collapse;
}

td {
  border: 1px solid grey;
  width: 2rem;
  height: 2rem;
  text-align: center;
  cursor: pointer;
  user-select: none;
}

tr {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

td {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.green {
  background-color: green;
}

.red {
  background-color: red;
}

.white {
  background-color: white;
}
<input type="button" value="Reset">

<br>
<br>

<table>
  <tr>
    <td>1</td>
    <td>2</td>
    <td>3</td>
  </tr>
  <tr>
    <td>4</td>
    <td>5</td>
    <td>6</td>
  </tr>
  <tr>
    <td>7</td>
    <td>8</td>
    <td>9</td>
  </tr>
</table>

<p id="demo"></p>