the first and last VISIBLE element in the ul list

Viewed 543

I have a scrollable list of items from which I want to detect the first and last item in the (ul li) list that are visible.

Code :

ul {
  list-style-type: none;
  width: 400px;
  height: 70px;
  overflow-x: scroll;
  margin: 0;
  padding: 0;
  overflow: auto;
  white-space: nowrap;
}

ul li {
  /* float: left; */
  display: inline-block;
  width: 35px;
  margin: 3px;
  height: 35px;
  border: 1px solid red;
  white-space: pre-wrap;
  padding: 5px;
}
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
  <li>Item 5</li>
  <li>Item 6</li>
  <li>Item 7</li>
  <li>Item 8</li>
  <li>Item 9</li>
  <li>Item 10</li>
  <li>Item 12</li>
  <li>Item 11</li>
  <li>Item 12</li>
  <li>Item 13</li>
  <li>Item 14</li>
  <li>Item 15</li>
  <li>Item 16</li>
</ul>

for example

here , the first is Item1 and the last is Item7:

enter image description here

here , the first is Item5 and the last is Item12

enter image description here

NOTE: I have to use only Javascript

3 Answers

Now that is a bit more clear ( thanks for update ), you have to calculate the number of hidden elements, something like :

var contentSize = 0;
for(var i = 0; contentSize < document.querySelector('ul').scrollLeft; i++)
{
    contentSize += document.querySelector('ul li')[i].getBoundingClientRect().width + 6;
}

First fully visible is on index i. Its not clear if you want the first fully visible or the first appearing.

Of course, you need to change the selector to target only the right element.

+6 is for the margins of the li, but again maybe fine tuned.

Try this.

// https://stackoverflow.com/a/41698614/17447
function isVisible(elem) {
  if (!(elem instanceof Element)) throw Error('DomUtil: elem is not an element.');
  const style = getComputedStyle(elem);
  if (style.display === 'none') return false;
  if (style.visibility !== 'visible') return false;
  if (style.opacity < 0.1) return false;
  if (elem.offsetWidth + elem.offsetHeight + elem.getBoundingClientRect().height +
    elem.getBoundingClientRect().width === 0) {
    return false;
  }
  const elemCenter = {
    x: elem.getBoundingClientRect().left + elem.offsetWidth / 2,
    y: elem.getBoundingClientRect().top + elem.offsetHeight / 2
  };
  if (elemCenter.x < 0) return false;
  if (elemCenter.x > (document.documentElement.clientWidth || window.innerWidth)) return false;
  if (elemCenter.y < 0) return false;
  if (elemCenter.y > (document.documentElement.clientHeight || window.innerHeight)) return false;
  let pointContainer = document.elementFromPoint(elemCenter.x, elemCenter.y);
  do {
    if (pointContainer === elem) return true;
  } while (pointContainer = pointContainer.parentNode);
  return false;
}

document.querySelector('#get-visible-item')
  .addEventListener('click', evt => {
    const elmList = document.querySelectorAll('#items li');
    let first = last = -1;
    for (let i = 0; i < elmList.length; i++) {
      const elm = elmList[i];
      if (first !== -1 && !isVisible(elm)) {
        last = i - 1;
        break;
      }
      if (first === -1 && isVisible(elm)) {
        first = i;
      }
      if (i === elmList.length - 1) {
        last = i;
      }
    }
    console.clear();
    console.log(elmList[first]);
    console.log(elmList[last]);
  });
ul {
  list-style-type: none;
  width: 400px;
  height: 70px;
  overflow-x: scroll;
  margin: 0;
  padding: 0;
  overflow: auto;
  white-space: nowrap;
}

ul li {
  /* float: left; */
  display: inline-block;
  width: 35px;
  margin: 3px;
  height: 35px;
  border: 1px solid red;
  white-space: pre-wrap;
  padding: 5px;
}
<ul id="items">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
  <li>Item 5</li>
  <li>Item 6</li>
  <li>Item 7</li>
  <li>Item 8</li>
  <li>Item 9</li>
  <li>Item 10</li>
  <li>Item 12</li>
  <li>Item 11</li>
  <li>Item 12</li>
  <li>Item 13</li>
  <li>Item 14</li>
  <li>Item 15</li>
  <li>Item 16</li>
</ul>
<button id="get-visible-item">Get Visible Item</button>

Item list :

<li id="first">Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li id="last">Item 5</li>

use js for detect :

document.getElementById("first").innerHTML = "First";
document.getElementById("last").innerHTML = "Last";
Related