【问题标题】:How to accurately determine if an element is scrollable?如何准确判断一个元素是否可滚动?
【发布时间】:2023-03-22 15:40:01
【问题描述】:

我正在开发一个自定义敲除绑定,该绑定确定是否正在滚动特定元素,并使用元素相对于视口的顶部更新绑定的 observable。目前,绑定似乎有效,但我担心是否在某些情况下无效。

HTML:

Scroll position: <span data-bind="text: scrollPosition"></span>

<div class="longdiv">    
    <p data-bind="scroll: scrollPosition">This is some text.</p>
    <div class="shim"></div>
</div>

CSS:

.longdiv {
    width: 200px;
    height: 200px;
    overflow: scroll;
    border: 1px solid black;
}

JS:

ko.bindingHandlers.scroll = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var firstScrollableContainer = null;

        var binding = allBindings.get('scroll');

        $(element).parents().each(function (i, parent) {
            if ($(parent).css('overflow')=='scroll') {
                firstScrollableContainer = parent;
                return false;
            }
        });

        firstScrollableContainer = firstScrollableContainer || window;                

        binding(element.getBoundingClientRect().top);

        $(firstScrollableContainer).scroll(function() {            
            binding(element.getBoundingClientRect().top);
        });
    }
};

var ViewModel = function() {
    var self = this;

    self.scrollPosition = ko.observable(0);
};

ko.applyBindings(new ViewModel());

JSFiddle

绑定获取元素并使用 jQuery 遍历父链查看父元素是否有溢出:滚动集。如果它找到一个带有溢出的 div:滚动,它会将一个事件处理程序绑定到该元素的滚动事件。如果它没有找到带有溢出:滚动的父级,则它会绑定到窗口的滚动事件。

所以我在找什么,给定一个结构如下的文档:

body > div > div > div > p

是最接近可滚动的 p 的包含元素,因此我可以将事件处理程序附加到它。

我的问题是:正在查看溢出:滚动一个足够的测试以查看是否可以滚动父元素?如果没有,我应该看什么?

编辑:根据您有用的 cmets 和答案,这是我想出的解决方案:

function scrollable(element) {
    var vertically_scrollable, horizontally_scrollable;

    var e = $(element);

     if (   e.css('overflow') == 'scroll' 
         || e.css('overflow') == 'auto'
         || e.css('overflowY') == 'scroll'
         || e.css('overflowY') == 'auto'
         || e.css('height') != 'none'
         || e.css('max-height') != 'none'                          
         ) {
         return true;
    } else {
        return false;
    }
}

【问题讨论】:

  • 我倾向于认为验证overflowoverflow-xoverflow-y 就足够了,但我不确定。检查这个:jsperf.com/jquery-scrollable
  • 注释:如果需要,可以使用 jQuery 的 scrollTop() 来获取滚动位置,而不是 getBoundingClientRect

标签: jquery html knockout.js


【解决方案1】:

您想知道一个元素是否可以滚动或当前是否可以滚动?

元素可以滚动吗?

如果元素具有固定的height(或max-height)并且overflow-yscrollauto,则它可以滚动。不过既然tell if an element's height is fixed or not不容易,查overflow-y大概就够了:

e.css('overflow-y') == 'scroll' || e.css('overflow-y') == 'auto'

元素现在可以滚动吗?

如果一个元素的scrollHeight 大于它的clientHeight 并且如果它有一个滚动条,那么它可以立即滚动,这可以通过比较clientWidthoffsetWidth 来确定(考虑边距和边框)或检查overflow-yscroll 还是auto

【讨论】:

  • 具体来说,我想知道它是否可以滚动,如果可以,我可以将事件处理程序附加到它。
  • 事实证明,很难判断一个元素是否具有固定高度。 e.css('height') 将始终返回一个像素值。
【解决方案2】:

这可能是最安全的解决方案(需要 jQuery,纯 JavaScript 见下文):

$.fn.isHScrollable = function () {
    return this[0].scrollWidth > this[0].clientWidth;
};

$.fn.isVScrollable = function () {
    return this[0].scrollHeight > this[0].clientHeight;
};

$.fn.isScrollable = function () {
    return this[0].scrollWidth > this[0].clientWidth || this[0].scrollHeight > this[0].clientHeight;
};

然后你可以像这样检查一个元素是否可以滚动:

$(parent).isScrollable();

如果不使用 jQuery,您可以实现如下功能:

function isScrollable(element) {
    return element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight;
};

var myParent = document.getElementById('myParent')
isScrollable(myParent)

【讨论】:

  • 对于具有overflow:hiddenoverflow:visible 的元素,此解决方案可以返回true → 这些情况可以具有scrollHeight > clientHeight 而不能滚动
【解决方案3】:

将两个答案合并在一起,并添加我自己的little something,这就是我用来检查垂直滚动的方法。它可以很容易地转换为其他情况。 (H & VH)

function isScrollable(e){
    if( e.scrollTopMax !== undefined )
        return e.scrollTopMax > 0; //All Hail Firefox and it's superior technology!

    if( e == document.scrollingElement ) //If what you're checking is BODY (or HTML depending on your css styles)
        return e.scrollHeight > e.clientHeight; //This is a special case.

    return e.scrollHeight > e.clientHeight && ["scroll", "auto"].indexOf(getComputedStyle(e).overflowY) >= 0



}

我在 Firefox 和 Chromium 上对此进行了测试。两者都是 Linux。不过,您可能仍想自己检查一下。

【讨论】:

  • 不需要额外检查溢出 (x/y),因为这是通过先前的检查自动确定的。
  • 有些元素没有被标记为可滚动,它们只是溢出了它们的父框而不是滚动。如果您省略检查样式的部分,则意味着您认为那些实际上是可滚动的。此外,Firefox 的 scrollTopMax 不是这样工作的,所以省略它也会引入不一致的行为。
  • 不确定这在移动设备上是如何工作的,移动设备具有滚动元素而没有可见的滚动条
  • 可见的滚动条就是这样,一个视觉元素。不应该对代码产生任何影响。 (桌面浏览器也可以隐藏滚动条)
【解决方案4】:

虽然这是一个老问题,但在多次尝试确定元素是否实际上可滚动失败后,我将发布我自己的(最新的)正确工作的答案。

function canScroll(el, scrollAxis) {
    if (0 === el[scrollAxis]) {
        el[scrollAxis] = 1;
        if (1 === el[scrollAxis]) {
            el[scrollAxis] = 0;
            return true;
        }
    } else {
        return true;
    }
    return false;
}

function isScrollableX(el) {
    return (el.scrollWidth > el.clientWidth) && canScroll(el, 'scrollLeft') && ('hidden' !== getComputedStyle(el).overflowX);
}

function isScrollableY(el) {
    return (el.scrollHeight > el.clientHeight) && canScroll(el, 'scrollTop') && ('hidden' !== getComputedStyle(el).overflowY);
}

function isScrollable(el) {
    return isScrollableX(el) || isScrollableY(el);
}

用途:

console.log(isScrollable(document.querySelector('#myElement')));

【讨论】:

    猜你喜欢
    • 2021-11-08
    • 2014-07-18
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 2015-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多