Search code examples
javascripthtmlcssonmouseoveronfocus

How to prevent automatic onmouseover after onfocus?


I have created an HTML table with three links in each of the rows. My goal is to highlight a row whenever the user (1) hovers over it or (2) enters a link by pressing tab. I would also like to make sure that only one row is highlighted at a time. Here is a simplified version of my HTML (show.htm):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <script type="text/javascript" src="show.js"></script>
    <link rel="stylesheet" type="text/css" href="show.css"/>
  </head>
  <body>
  <h2>Basic Information:</h2>
  <table>
    <tr class="data">
      <td>London:</td>
      <td><a href="http://example.com/london_001.htm">Part 1</a></td>
      <td><a href="http://example.com/london_002.htm">Part 2</a></td>
      <td><a href="http://example.com/london_003.htm">Part 3</a></td>
    </tr>
    <tr class="data">
      <td>New York:</td>
      <td><a href="http://example.com/newyork_001.htm">Part 1</a></td>
      <td><a href="http://example.com/newyork_002.htm">Part 2</a></td>
      <td><a href="http://example.com/newyork_003.htm">Part 3</a></td>
    </tr>
    <tr class="data">
      <td>Tokyo:</td>
      <td><a href="http://example.com/tokyo_001.htm">Part 1</a></td>
      <td><a href="http://example.com/tokyo_002.htm">Part 2</a></td>
      <td><a href="http://example.com/tokyo_003.htm">Part 3</a></td>
    </tr>
    <tr class="data">
      <td>Rio de Janeiro:</td>
      <td><a href="http://example.com/riodejaneiro_001.htm">Part 1</a></td>
      <td><a href="http://example.com/riodejaneiro_002.htm">Part 2</a></td>
      <td><a href="http://example.com/riodejaneiro_003.htm">Part 3</a></td>
    </tr>
    <tr class="data">
      <td>Melbourne:</td>
      <td><a href="http://example.com/melbourne_001.htm">Part 1</a></td>
      <td><a href="http://example.com/melbourne_002.htm">Part 2</a></td>
      <td><a href="http://example.com/melbourne_003.htm">Part 3</a></td>
    </tr>
  </table>
  </body>
</html>

Here is the Javascript (show.js):

window.onload = function(){
  var rows = document.getElementsByClassName('data');
  var links = document.getElementsByTagName('a');
  var len = rows.length;
  var old_index = -1;
  var set_color = function(index, color){
    return function(){
      if(index !== old_index){
        if(old_index !== -1){
          rows[old_index].style.backgroundColor = null;
        }
        rows[index].style.backgroundColor = color;
        old_index = index;
      }
    }
  }
  var i, j;
  for(i = 0; i < len; i++){
    rows[i].onmouseover = set_color(i, '#FFFFBB');
    rows[i].onmouseout = set_color(i, null);
    for(j = 0; j < 3; j++){
      links[i*3+j].onfocus = set_color(i, '#FFFFBB');
      links[i*3+j].onblur = set_color(i, null);
    }
  }
}

And, finally, here is the CSS (show.css):

table{
  margin:auto;
  width:98%;
  border-collapse:collapse;
  border:none;
}
td{
  border-top:1px solid gray;
  border-bottom:1px solid gray;
  border-left:none;
  border-right:none;
  vertical-align:center;
  font-weight:bold;
}
.data{
  background-color:rgba(120, 120, 240, 0.4);
  font-family:Tahoma;
  font-size:16px;
}
.data>td{
    padding:6px 16px;
}

Whenever the page is loaded and I press tab, the first link receives focus but the row on which the mouse pointer rests (not necessarily the first row) gets highlighted. It seems that the onmouseover event fires immediately after the onfocus, even though no mouse movement is actually made. How to tackle this issue (without using any external library like JQuery)? Any help will be appreciated.


Update:

I have modified my Javascript code in order to detect actual mouse movement (borrowing the idea from Nick Bull’s answer). But the problem has remained. I need further help.

window.onload = function(){
  var rows = document.getElementsByClassName('data');
  var links = document.getElementsByTagName('a');
  var len = rows.length;
  var old_index = -1;
  var old_coords = {X:event.screenX, Y:event.screenY};
  var set_color = function(index, color, keyboard_event){
    return function(){
      if(keyboard_event || event.screenX !== old_coords.X || event.screenY !== old_coords.Y){
        if(index !== old_index){
          if(old_index !== -1){
            rows[old_index].style.backgroundColor = null;
          }
          rows[index].style.backgroundColor = color;
          old_index = index;
          if(!keyboard_event){
            old_coords.X = event.screenX;
            old_coords.Y = event.screenY;
          }
        }
      }
    }
  }
  var i, j;
  for(i = 0; i < len; i++){
    rows[i].onmouseover = set_color(i, '#FFFFBB', false);
    rows[i].onmouseout = set_color(i, null, false);
    for(j = 0; j < 3; j++){
      links[i*3+j].onfocus = set_color(i, '#FFFFBB', true);
      links[i*3+j].onblur = set_color(i, null, true);
    }
  }
}

The document loads:

screenshot

The first tab is pressed:

screenshot


Solution

  • This is how I ultimately killed the demon (ugly enough, but works nonetheless):
    (1) I created two different variables for the last hovered index and the last focused index
    (2) I created four different functions for onmouseover, onmouseout, onfocus and onblur
    (3) I added a timeout to ensure that onmouseover does not fire immediately after onfocus.

    Here is the modified Javascript code:

    window.onload = function(){
      var rows = document.getElementsByClassName('data');
      var links = document.getElementsByTagName('a');
      var len = rows.length;
      var color = '#FFFFBB';
      var hovered_index = -1;
      var focused_index = -1;
      var mouseover_allowed = true;
      var set_color_onmouseover = function(index){
        return function(){
          if(mouseover_allowed){
            if(focused_index !== -1){
              rows[focused_index].style.backgroundColor = null;
            }
            rows[index].style.backgroundColor = color;
            hovered_index = index;
          }
        }
      }
      var set_color_onmouseout = function(index){
        return function(){
          if(hovered_index !== -1){
            rows[index].style.backgroundColor = null;
            hovered_index = -1;
          }
        }
      }
      var set_color_onfocus = function(index){
        return function(){
          if(hovered_index !== -1){
            rows[hovered_index].style.backgroundColor = null;
          }
          rows[index].style.backgroundColor = color;
          focused_index = index;
          // disable onmouseover for the next 100 milliseconds
          mouseover_allowed = false;
          setTimeout(function(){ mouseover_allowed = true; }, 100);
        }
      }
      var set_color_onblur = function(index){
        return function(){
          if(focused_index !== -1){
            rows[index].style.backgroundColor = null;
            focused_index = -1;
          }
        }
      }
      var i, j;
      for(i = 0; i < len; i++){
        rows[i].onmouseover = set_color_onmouseover(i);
        rows[i].onmouseout = set_color_onmouseout(i);
        for(j = 0; j < 3; j++){
          links[i*3+j].onfocus = set_color_onfocus(i);
          links[i*3+j].onblur = set_color_onblur(i);
        }
      }
    }