HTML Table: overflow hidden for horizontal scroll then blocks tooltip from showing outside of table

Viewed 1872

TL;DR: Cannot correctly position & display tooltips in a table due to overflow: hidden.

I have created a simple reproducible example of a table whose tooltips are not displaying as intended. The issue exists in part because the table requires overflow: scroll in order for the horizontal scroll of the table to work as intended. Here is an example:

.gap {
  height: 50px
}

div.table { 
  max-width: 200px;

  overflow: scroll;
  border: 2px solid blue;
  display: inline-block;
  font-family: 'Roboto Mono';
  font-weight: 700;
}

.tr {
  display:flex;
}

.td, .th {
  border: 1px solid black;
  min-width: 60px;
  height: 50px;
}

.th {
  position: relative;
  font-size: 13px;
  padding: 2px;
  background-color: #888888;
  text-align: center;
  border-right: 1px solid #555555;
  display: inline-block;
  box-sizing: border-box;
}

.tip-wrapper {
  position: absolute;
}

.tip0 {
  visibility: hidden;
  position: absolute;
  top: -60%;
  z-index: 1;
  max-width: 325px;
  min-width: 160px;
  color: #333333;
  text-align: left;
  background: white;
  border: 1px solid black;
}

.tip1 {
  visibility: hidden;
  position: fixed;
  z-index: 1;
  max-width: 325px;
  min-width: 160px;
  color: #333333;
  text-align: left;
  background: white;
  border: 1px solid black;
}

.tooltip:hover .tip0, .tooltip:hover .tip1 {
  visibility: visible;
}
<div class='gap'>.</div>
<div class='table'>
  <div class='table-headers'>
    <div class='tr'>
      <div class='th tooltip'>
        Z
        <div class='tip0'>
          Heres the ToolTip For Column Z, getting cut off (bad)
        </div>
      </div>
      <div class='th'>Y</div>
      <div class='th tooltip'>
        X
        <div class='tip-wrapper'>
          <div class='tip1'>
            Heres the Tooltip For Column X, which doesn't stay at the top (bad)
          </div>
        </div>
      </div>
      <div class='th'>W</div>
      <div class='th'>V</div>
    </div>
  </div>
  <div class='table-body'>
    <div class='tr'>
    </div>
    <div class='tr'>
      <div class='td stick'>A</div>
      <div class='td'>B</div>
      <div class='td'>C</div>
      <div class='td'>D</div>
      <div class='td'>E</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
  </div>
</div>

In the table above, the Z and X header cells have tooltips that appear on, however. However, there are issues with both of these tooltips currently:

Column Z: Because of overflow: hidden, this tooltip is getting cut off. It is important that the table can scroll horizontally, so I cannot remove this overflow CSS property. However, its also important that the tooltip hovers above the top of the table, so as to not block the content of the table.

Column X: I tried this approach from this other post. Method 1. This allows the tooltip outside of the overflow: hidden. However, when I scroll this table vertically, the tooltip moves as well, which is not good. The tooltip should remain at the same height always, just above the top of the table.

I was hoping that this basic "tooltips inside the table headers" would work, but it isn't so far. Because of this, I am considering creating a separate tooltip div outside of the table's HTML, and using onMouseOver() and onMouseOut() handler functions to position the tooltip at the mouse location, and populate the tooltip with text. Maybe that is my only approach?

4 Answers

You are missing a .tip-wrapper element on tip0, if you add this and set the top position of .tip-wrapper using calc() to -(th height) - (top threshold in rem) in this case calc(-50px - 1.5rem) then the problem is solved and you have the tooltip placed on top of the table.

You could use dynamic units in th height such as vh, vmax, %. Additionally you would need some @media queries to fine-tune the position of the tooltip.

.td,
.th {
  border: 1px solid black;
  min-width: 10%;
  height: 5vh;
}
.tip-wrapper {
  position: absolute;
  top: calc(-5vh - 1.5rem);
}

.gap {
  height: 100px
}

div.table {
  max-width: 200px;
  overflow: scroll;
  border: 2px solid blue;
  display: inline-block;
  font-family: 'Roboto Mono';
  font-weight: 700;
}

.tr {
  display: flex;
}

.td,
.th {
  border: 1px solid black;
  min-width: 60px;
  height: 50px;
}

.th {
  position: relative;
  font-size: 13px;
  padding: 2px;
  background-color: #888888;
  text-align: center;
  border-right: 1px solid #555555;
  display: inline-block;
  box-sizing: border-box;
}

.tip-wrapper {
  position: absolute;
  top: calc(-50px - 1.5rem);
}

.tip0 {
  visibility: hidden;
  position: fixed;
  z-index: 1;
  max-width: 325px;
  min-width: 160px;
  color: #333333;
  text-align: left;
  background: white;
  border: 1px solid black;
}

.tip1 {
  visibility: hidden;
  position: fixed;
  z-index: 1;
  max-width: 325px;
  min-width: 160px;
  color: #333333;
  text-align: left;
  background: white;
  border: 1px solid black;
}

.tooltip:hover .tip0,
.tooltip:hover .tip1 {
  visibility: visible;
}
<div class='gap'>Gap</div>
<div class='table'>
  <div class='table-headers'>
    <div class='tr'>
      <div class='th tooltip'>
        Z
        <div class='tip-wrapper'>
          <div class='tip0'>
            Tip For Column Z is a long tip with enough text to overflow above the page top
          </div>
        </div>
      </div>
      <div class='th'>Y</div>
      <div class='th tooltip'>
        X
        <div class='tip-wrapper'>
          <div class='tip1'>
            Tip For Column Z is a long tip with enough text to overflow above the page top
          </div>
        </div>
      </div>
      <div class='th'>W</div>
      <div class='th'>V</div>
    </div>
  </div>
  <div class='table-body'>
    <div class='tr'>
    </div>
    <div class='tr'>
      <div class='td stick'>A</div>
      <div class='td'>B</div>
      <div class='td'>C</div>
      <div class='td'>D</div>
      <div class='td'>E</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
  </div>
</div>

Solution

You can mimic your table canvas with a sibling element, then use custom overflow properties on that child to achieve whatever effect you'd like:

document.addEventListener('DOMContentLoaded', _ => {
  const tool = document.querySelector('.tooltip-canvas > tooltip')
  const toolEls = Array.from(document.querySelectorAll('.table [tooltip]'))
  toolEls.forEach(t => {
    t.onmouseenter = _ => {
      tool.setAttribute('tooltip', t.getAttribute('tooltip'))
      tool.style.left = `${t.clientLeft}px`
      tool.style.top = `${t.clientTop - 2}px`
    }
    t.onmouseleave = _ => tool.removeAttribute('tooltip')
  })
})
.gap {
  height: 50px
}

.table-wrap {
  position: relative;
  display: inline-block;
}

.table-wrap > .tooltip-canvas {
  position: absolute;
  pointer-events: none;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.table-wrap > .tooltip-canvas > tooltip {
  content: attr(tooltip);
  position: absolute;
  z-index: 1;
  max-width: 325px;
  min-width: 160px;
  color: #333333;
  text-align: left;
  background: white;
  border: 1px solid black;
  opacity: 0;
  transform: translateY(-100%);
  transition: opacity .25s;
}
.table-wrap > .tooltip-canvas > tooltip[tooltip] {
  opacity: 1;
}

.table-wrap > .tooltip-canvas > tooltip:before {
  content: attr(tooltip)
}

div.table { 
  max-width: 200px;
  overflow: scroll;
  border: 2px solid blue;
  display: inline-block;
  font-family: 'Roboto Mono';
  font-weight: 700;
}

.tr {
  display:flex;
}

.td, .th {
  border: 1px solid black;
  min-width: 60px;
  height: 50px;
}

.th {
  position: relative;
  font-size: 13px;
  padding: 2px;
  background-color: #888888;
  text-align: center;
  border-right: 1px solid #555555;
  display: inline-block;
  box-sizing: border-box;
}
<div class='gap'>.</div>
<section class='table-wrap'>
  <div class='table'>
    <div class='table-headers'>
      <div class='tr'>
        <div class='th' tooltip='Heres the ToolTip For Column Z, getting cut off (bad)'>
          Z
        </div>
        <div class='th'>Y</div>
        <div class='th' tooltip='Heres the Tooltip For Column X, which doesn&spquot;t stay at the top (bad)'>
          X
        </div>
        <div class='th'>W</div>
        <div class='th'>V</div>
      </div>
    </div>
    <div class='table-body'>
      <div class='tr'>
      </div>
      <div class='tr'>
        <div class='td stick'>A</div>
        <div class='td'>B</div>
        <div class='td'>C</div>
        <div class='td'>D</div>
        <div class='td'>E</div>
      </div>
      <div class='tr'>
        <div class='td'>F</div>
        <div class='td'>G</div>
        <div class='td'>H</div>
        <div class='td'>I</div>
        <div class='td'>J</div>
      </div>
      <div class='tr'>
        <div class='td'>F</div>
        <div class='td'>G</div>
        <div class='td'>H</div>
        <div class='td'>I</div>
        <div class='td'>J</div>
      </div>
      <div class='tr'>
        <div class='td'>F</div>
        <div class='td'>G</div>
        <div class='td'>H</div>
        <div class='td'>I</div>
        <div class='td'>J</div>
      </div>
      <div class='tr'>
        <div class='td'>F</div>
        <div class='td'>G</div>
        <div class='td'>H</div>
        <div class='td'>I</div>
        <div class='td'>J</div>
      </div>
      <div class='tr'>
        <div class='td'>F</div>
        <div class='td'>G</div>
        <div class='td'>H</div>
        <div class='td'>I</div>
        <div class='td'>J</div>
      </div>
    </div>
  </div>
  <aside class='tooltip-canvas'>
    <tooltip></tooltip>
  </aside>
</section>

Just change the top value in tip0:

.tip0 {
  visibility: hidden;
  position: absolute;
  top: 40%;
  z-index: 1;
  max-width: 325px;
  min-width: 160px;
  color: #333333;
  text-align: left;
  background: white;
  border: 1px solid black;
}

.gap {
  height: 100px
}

div.table { 
  max-width: 200px;

  overflow: scroll;
  border: 2px solid blue;
  display: inline-block;
  font-family: 'Roboto Mono';
  font-weight: 700;
}

.tr {
  display:flex;
}

.td, .th {
  border: 1px solid black;
  min-width: 60px;
  height: 50px;
}

.th {
  position: relative;
  font-size: 13px;
  padding: 2px;
  background-color: #888888;
  text-align: center;
  border-right: 1px solid #555555;
  display: inline-block;
  box-sizing: border-box;
}

.tip-wrapper {
  position: absolute;
}

.tip0 {
  visibility: hidden;
  position: fixed;
  z-index: 1;
  max-width: 325px;
  min-width: 160px;
  color: #333333;
  text-align: left;
  background: white;
  border: 1px solid black;
}

.tip1 {
  visibility: hidden;
  position: fixed;
  z-index: 1;
  max-width: 325px;
  min-width: 160px;
  color: #333333;
  text-align: left;
  background: white;
  border: 1px solid black;
}

.tooltip:hover .tip0, .tooltip:hover .tip1 {
  visibility: visible;
}
<div class='gap'>Gap</div>
<div class='table'>
  <div class='table-headers'>
    <div class='tr'>
      <div class='th tooltip'>
        Z
        <div class='tip0'>
          Tip For Column Z is a long tip with enough text to overflow above the page top
        </div>
      </div>
      <div class='th'>Y</div>
      <div class='th tooltip'>
        X
        <div class='tip-wrapper'>
          <div class='tip1'>
            Tip For Column Z is a long tip with enough text to overflow above the page top
          </div>
        </div>
      </div>
      <div class='th'>W</div>
      <div class='th'>V</div>
    </div>
  </div>
  <div class='table-body'>
    <div class='tr'>
    </div>
    <div class='tr'>
      <div class='td stick'>A</div>
      <div class='td'>B</div>
      <div class='td'>C</div>
      <div class='td'>D</div>
      <div class='td'>E</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
    <div class='tr'>
      <div class='td'>F</div>
      <div class='td'>G</div>
      <div class='td'>H</div>
      <div class='td'>I</div>
      <div class='td'>J</div>
    </div>
  </div>
</div>

For tooltip above the table

Remove top attribute and change the position attribute in .tip0 as below

.tip0 {
        visibility: hidden;
        position: fixed;
        margin: -56px -4px;
        z-index: 1;
        max-width: 325px;
        min-width: 160px;
        color: #333333;
        text-align: left;
        background: white;
        border: 1px solid black;
    }

And add margin in .tip1 as below

.tip1 {
    visibility: hidden;
    position: fixed;
    margin: -56px -4px;
    z-index: 1;
    max-width: 325px;
    min-width: 160px;
    color: #333333;
    text-align: left;
    background: white;
    border: 1px solid black;
}

For displaying tooltip within table

Add margin attribute as below inside .tip0 and .tip1 to keep tooltip inside table.

margin: -17px 0px;

and remove top attribute from .tip0

Related