Точно определить, прокручивается ли элемент

Я работаю над пользовательской привязкой нокаута, которая определяет, прокручивается ли конкретный элемент, и обновляет границу, наблюдаемую, с верхом элемента относительно области просмотра. Прямо сейчас привязка, кажется, работает, но у меня есть некоторые беспокойства о том, есть ли обстоятельства, когда это не будет.

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, чтобы пройти по родительской цепочке и посмотреть, есть ли у родительского элемента overflow: scroll set. Если он находит div с переполнением: scroll, он связывает обработчик события с этим событием прокрутки элемента. Если он не находит родителя с переполнением: scroll, он привязывается к событию прокрутки окна.

Итак, что я ищу, учитывая документ, структурированный так:

body > div > div > div > p

является ближайшим к p содержащим элементом, который можно прокручивать, чтобы я мог прикрепить к нему обработчик событий.

Мой вопрос: смотрит на переполнение: прокрутите достаточный тест, чтобы увидеть, можно ли прокрутить родительский элемент? Если нет, на что мне смотреть?

РЕДАКТИРОВАТЬ: На основе ваших полезных комментариев и ответов, вот решение, которое я придумал:

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;
    }
}

Ответ 1

Вы хотите узнать, может ли элемент прокручиваться или может прокручиваться в настоящее время?

Можно ли прокручивать элемент?

Элемент может прокручиваться, если он имеет фиксированный height (или max-height), а overflow-y - scroll или auto. Но так как нелегко указать, если высота элемента фиксирована или нет, вероятно, достаточно просто проверить overflow-y:

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

Может ли элемент прокручиваться прямо сейчас?

Элемент может прокручиваться прямо сейчас, если его scrollHeight больше его clientHeight, и если у него есть полоса прокрутки, которая может быть определена путем сравнения clientWidth и offsetWidth (с учетом полей и границ) или если overflow-y равен scroll или auto.

Ответ 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)

Ответ 3

Объединяя два ответа вместе и добавляя что-то свое, я использую это для проверки вертикальной прокрутки. Это может быть легко преобразовано для других случаев. (H & VH)

function isScrollable(e){
    if( e.scrollTopMax !== undefined )
        return e.scrollTopMax > 0; //All Hail Firefox and it 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. Вы все еще можете проверить их сами.