5

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

naveen
  • 53,448
  • 46
  • 161
  • 251
  • 3
    You mean, the first and last item inside the view of the scroll? – Udaya Prakash Jul 26 '20 at 07:45
  • You can use id for first and last li tags and then use js to detect it – Govi-Boy Jul 26 '20 at 07:46
  • @Udaya Parkash- Yes; –  Jul 26 '20 at 07:47
  • 1
    Does this answer your question? [Check if element is visible in DOM](https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom) – Muhammad Vakili Jul 26 '20 at 07:49
  • 1
    When you say "detect", for which purpose ? querySelector('li:first-child') and querySelector('li:last-child') will do the trick for javascript. EDIT - or ( its not clear ), you want the first and last VISIBLE element in the ul list ? Then I guess that your only way is to calculate the scrollLeft value and count how many elements are hidden, then take the next one. – iguypouf Jul 26 '20 at 07:56
  • 2
    You should be able to do something using an [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). You can set it up to trigger whenever one of the list's children *intersects* with the visible part of it (or no longer intersects, which means it's scrolled out). – Touffy Jul 26 '20 at 08:10

3 Answers3

1

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.

iguypouf
  • 770
  • 4
  • 15
0

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>
naveen
  • 53,448
  • 46
  • 161
  • 251
-1

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";
Govi-Boy
  • 89
  • 10
  • 3
    @ Govi-Boy .I want to detect the first and last item in the displayed list . your code results in the first and last item in the whole list . –  Jul 26 '20 at 07:54
  • @SamiBan then you can use Ids for 1,7,5,12 item li tags? – Govi-Boy Jul 26 '20 at 07:58