确定元素是否可滚动

问题描述 投票:0回答:2

我正在寻找一种方法来确定元素是否可以在给定方向上滚动。也就是说,如果我可以调用

Element.scrollBy
来滚动它。

我广泛搜索并最终得到:

/**
 * Whether the element can be scrolled.
 * @param {HTMLElement} el The element.
 * @param {boolean} vertical Whether the scroll is vertical.
 * @param {boolean} plus Whether the scroll is positive (down or right).
 * @returns {boolean} Whether the element can be scrolled.
 */
function canScroll(el, vertical = true, plus = true) {
    const style = window.getComputedStyle(el);
    const overflow = vertical ? style.overflowY : style.overflowX;
    const scrollSize = vertical ? el.scrollHeight : el.scrollWidth;
    const clientSize = vertical ? el.clientHeight : el.clientWidth;
    const scrollPos = vertical ? el.scrollTop : el.scrollLeft;
    const isScrollable = scrollSize > clientSize;
    const canScrollFurther = plus
        ? scrollPos + clientSize < scrollSize
        : scrollPos > 0;
    return (
        isScrollable &&
        canScrollFurther &&
        !overflow.includes("visible") &&
        !overflow.includes("hidden")
    );
}

该片段在大多数场合下效果很好,但不幸的是并非所有场合。这是 CodePen 上的一个 示例,在

document.body
document.body.clientHeight !== document.body.scrollHeight
上调用它。在这种情况下,它返回
true
,而 should 返回
false
,因为调用
document.body.scrollBy({top: 100})
不会产生任何结果。

如何改进这个

canScroll
函数,以便它能够正确处理给定的示例?

javascript html css scroll scrollable
2个回答
0
投票

我想出了一个相当老套的解决方案。主要思想是尝试滚动,并检测滚动是否成功:

/**
 * Detect whether the element can be scrolled using a hacky detection method.
 * @param {HTMLElement} el The element.
 * @param {boolean} vertical Whether the scroll is vertical.
 * @param {boolean} plus Whether the scroll is positive (down or right).
 * @returns {boolean} Whether the element can be scrolled.
 */
function hackyDetect(el, vertical = true, plus = true) {
    const attrs = vertical ? ["top", "scrollTop"] : ["left", "scrollLeft"];
    const delta = plus ? 1 : -1;
    const before = el[attrs[1]]; // Determine `scrollTop`/`scrollLeft` before trying to scroll
    el.scrollBy({ [attrs[0]]: delta, behavior: "instant" }); // Try to scroll in the specified direction
    const after = el[attrs[1]]; // Determine `scrollTop`/`scrollLeft` after we've scrolled
    if (before === after) return false;
    else {
        el.scrollBy({ [attrs[0]]: -delta, behavior: "instant" }); // Scroll back if applicable
        return true;
    }
}

缺点是,如果可滚动,它会中断

el
上正在进行的滚动,并且可能效率较低。


0
投票

正如您所发现的,检查

clientHeight
是否小于
scrollHeight
并不总是可靠的。我认为最好的方法是首先检查当前滚动位置是否不为 0,如果是,则该元素是可滚动的。否则,您可以尝试滚动 1 像素,再次检查偏移量并恢复为 0。这不应导致任何可见的移动,因为这一切都发生在单个帧中。

这是一个工作示例。

// axis is one of "x" or "y", or if omitted, will check both
const isScrollable = (element, axis) => {
  if (!axis) {
    return isScrollable(element, "x") || isScrollable(element, "y");
  }

  const offset = axis === "x" ? "Left" : "Top";

  // Checking if clientHeight < scrollHeight is not always reliable
  if (element[`scroll${offset}`]) {
    return true;
  }

  element[`scroll${offset}`] = 1;
  const canScroll = element[`scroll${offset}`] > 0;
  element[`scroll${offset}`] = 0;
  return canScroll;
};
© www.soinside.com 2019 - 2024. All rights reserved.