skip to Main Content

I’m trying to create some kind of toolkit for my index action in Rails. I would like a new div to appear when the mouse hovers over a specific cell in the table but it does not work

There are no errors in the browser dev tools but just but nothing happens. Where is the issue?

document.addEventListener('turbo:load', function() {
  document.querySelector('table').addEventListener('mouseover', function(event) {
    let target = event.target;
    while (target && target.tagName !== 'td') {
      target = target.parentElement;
    }

    if (target && target.hasAttribute('dataTooltip')) {
      if (!target.querySelector('.tooltip-text')) {
        const tooltipText = document.createElement('div');
        tooltipText.className = 'tooltip-text';
        tooltipText.innerHTML = target.getAttribute('dataTooltip');
        target.classList.add('tooltip');
        target.appendChild(tooltipText);
      }
    }
  });

  document.querySelector('table').addEventListener('mouseout', function(event) {
    let target = event.target;
    while (target && target.tagName !== 'td') {
      target = target.parentElement;
    }

    if (target && target.hasAttribute('dataTooltip')) {
      const tooltipText = target.querySelector('.tooltip-text');
      if (tooltipText) {
        tooltipText.romove();
      }
    }
  });
});
.tooltip {
  position: relative;
  display: inline-block;
}

.tooltip .tooltip-text {
  visibility: hidden;
  background-color: #555;
  color: #fff;
  text-align: left;
  border-radius: 10px;
  padding: 10px;
  position: absolute;
  z-index: 1;
  bottom: 125%;
  left: 50%;
  margin-left: -50px;
  opacity: 0;
  transition: opacity 0.3s;
  width: auto;
  max-width: 300px;
}

.tooltip:hover .tooltip-text {
  visibility: visible;
  opacity: 1;
}

.tooltip .tooltip-text table {
  width: 100%;
  border-collapse: collapse;
}

.tooltip .tooltip-text table,
.tooltip .tooltip-text td,
.tooltip .tooltip-text th {
  border: 1px solid #ddd;
  padding: 8px;
}

.tooltip .tooltip-text th {
  padding-top: 12px;
  padding-bottom: 12px;
  text-align: left;
  background-color: #333;
  color: white;
}
<table>
  <tr>
    <td dataTooltip="<table><tr><td><%= some.data %></td></tr><tr><td><%= some.data2 %></td></tr></table>">
      some.data3
    </td>
  </tr>
</table>

2

Answers


  1. I did some improvements to the code in order for it to work:

    1. replaced turbo:load with DOMContentLoaded in order to load properly when the page is done loading.

    2. Replaces tooltipText.romove(); with tooltipText.remove(); (that was a syntax error)

    3. Renamed dataTooltip with data-Tooltip just for some consistency with the html5 data attribute standards.

    4. minor changes in css to properly display the tooltip.

    5. Added some content sanitizing to ensure that whatever is inside data-tooltip is rendered as HTML.

    Hope it was what you asked for.

        document.addEventListener('DOMContentLoaded', function() {
          const table = document.querySelector('table');
    
          if (table) {
            table.addEventListener('mouseover', function(event) {
              let target = event.target;
              while (target && target.tagName !== 'TD') {
                target = target.parentElement;
              }
    
              if (target && target.hasAttribute('data-tooltip')) {
                if (!target.querySelector('.tooltip-text')) {
                  const tooltipText = document.createElement('div');
                  tooltipText.className = 'tooltip-text';
                  tooltipText.innerHTML = target.getAttribute('data-tooltip');
                  target.classList.add('tooltip');
                  target.appendChild(tooltipText);
                }
              }
            });
    
            table.addEventListener('mouseout', function(event) {
              let target = event.target;
              while (target && target.tagName !== 'TD') {
                target = target.parentElement;
              }
    
              if (target && target.hasAttribute('data-tooltip')) {
                const tooltipText = target.querySelector('.tooltip-text');
                if (tooltipText) {
                  tooltipText.remove();
                }
              }
            });
          }
        });
        /* CSS remains mostly the same */
        .tooltip {
          position: relative;
          display: inline-block;
        }
    
      .tooltip .tooltip-text {
        visibility: hidden;
        background-color: #555;
        color: #fff;
        text-align: left;
        border-radius: 10px;
        padding: 10px;
        position: absolute;
        z-index: 1;
        top: 100%;
        left: 50%;
        transform: translateX(-50%);
        opacity: 0;
        transition: opacity 0.3s;
        width: auto;
        max-width: 300px;
      }
    
        .tooltip:hover .tooltip-text {
          visibility: visible;
          opacity: 1;
        }
    
        .tooltip .tooltip-text table {
          width: 100%;
          border-collapse: collapse;
        }
    
        .tooltip .tooltip-text table,
        .tooltip .tooltip-text td,
        .tooltip .tooltip-text th {
          border: 1px solid #ddd;
          padding: 8px;
        }
    
        .tooltip .tooltip-text th {
          padding-top: 12px;
          padding-bottom: 12px;
          text-align: left;
          background-color: #333;
          color: white;
        }
      <table>
        <tr>
          <td data-tooltip="<table><tr><td>data1</td></tr><tr><td>data2</td></tr></table>">
            data3
          </td>
        </tr>
      </table>
    Login or Signup to reply.
    1. The tagName property for HTML elements is always in upper case. Meaning you should change "target.tagName !== ‘td’" to "target.tagName !== ‘TD’". https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName

    2. You are adding the event listeners to the table element which doesn’t make sense since you are searching for the td element by accessing the parent repeatedly whereas the td element is inside the table elements and not vise versa. This means the listener will run unnecessary when the table element has mouseover/mouseout triggered.

    Because events propagate, even when the td has mouseover/mouseout triggered the table’s event listeners will also catch those events and thus your code will work but ideally I suggest you add the event handlers directly to the td element itself instead by changing document.querySelector(‘table’) to document.querySelector(‘td’).

    Here is a working snippet where I also gave margin to the table in order for you to actually see the tooltip and removed the initial turbo:load listener in order for it to work here.

      document.querySelector('td').addEventListener('mouseover', function(event) {
        let target = event.target;
        while (target && target.tagName !== 'TD') {
          target = target.parentElement;
        }
    
        if (target && target.hasAttribute('dataTooltip')) {
          if (!target.querySelector('.tooltip-text')) {
            const tooltipText = document.createElement('div');
            tooltipText.className = 'tooltip-text';
            tooltipText.innerHTML = target.getAttribute('dataTooltip');
            target.classList.add('tooltip');
            target.appendChild(tooltipText);
          }
        }
      });
    
      document.querySelector('td').addEventListener('mouseout', function(event) {
        let target = event.target;
        while (target && target.tagName !== 'TD') {
          target = target.parentElement;
        }
    
        if (target && target.hasAttribute('dataTooltip')) {
          const tooltipText = target.querySelector('.tooltip-text');
          if (tooltipText) {
            tooltipText.remove();
          }
        }
      });
    .tooltip {
      position: relative;
      display: inline-block;
    }
    
    .tooltip .tooltip-text {
      visibility: hidden;
      background-color: #555;
      color: #fff;
      text-align: left;
      border-radius: 10px;
      padding: 10px;
      position: absolute;
      z-index: 1;
      bottom: 125%;
      left: 50%;
      margin-left: -50px;
      opacity: 0;
      transition: opacity 0.3s;
      width: auto;
      max-width: 300px;
    }
    
    .tooltip:hover .tooltip-text {
      visibility: visible;
      opacity: 1;
    }
    
    .tooltip .tooltip-text table {
      width: 100%;
      border-collapse: collapse;
    }
    
    .tooltip .tooltip-text table,
    .tooltip .tooltip-text td,
    .tooltip .tooltip-text th {
      border: 1px solid #ddd;
      padding: 8px;
    }
    
    .tooltip .tooltip-text th {
      padding-top: 12px;
      padding-bottom: 12px;
      text-align: left;
      background-color: #333;
      color: white;
    }
    
    .main-table {
      margin: 200px;
    }
    <table class='main-table'>
      <tr>
        <td dataTooltip="<table><tr><td><%= some.data %></td></tr><tr><td><%= some.data2 %></td></tr></table>">
          some.data3
        </td>
      </tr>
    </table>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search