在RTL模式下获得可滚动DIV的视图的更好方法

Better way to get the viewport of a scrollable DIV in RTL mode?

本文关键字:DIV 视图 更好 方法 滚动 模式 RTL      更新时间:2023-09-26

我需要一个更好的方法来计算可滚动div的视口。

在正常情况下,我会使用以下属性:(scrollLeft, scrollTop, clientWidth, clienttheight)

使用这些数字,我可以准确地确定一个可滚动的DOM元素的视口的哪一部分是当前可见的,我使用这个信息异步加载的东西,当滚动到水平或垂直的内容时,对用户可见的需求。当DIV的内容非常大时,这将避免由于加载太多DOM元素而导致浏览器崩溃的尴尬bug。

我的组件已经工作了一段时间了,现在没有问题,这个版本我们正在引入RTL支持。现在,由于浏览器的不一致,一切都被打乱了。

为了演示,我创建了一个简单的示例,它将输出JSFiddle中可滚动元素的scrollLeft属性。

这个简单的可滚动元素的scrollLeft属性的行为在不同浏览器之间是不一致的。我试过的3个主要浏览器都有不同的表现。

  1. FF-latest scrollLeft从0开始,当向左滚动时变为负值
  2. ie9 scrollLeft从0开始,当向左滚动时变为正
  3. Chrome-latest scrollLeft从一个更高的数字开始,当向左滚动时变为0

我想避免像if(ie){...}else if(ff){...}else if (chrome){...}这样的代码,这将是可怕的,并且在浏览器改变行为的情况下无法长期维护。

有没有更好的方法来精确地找出DIV的哪一部分是当前可见的?

也许有一些其他可靠的DOM属性,而不是scrollLeft?

也许有一个jQuery插件会为我做,记住它是哪个浏览器版本?

也许有一种技术,我可以用它来找出哪些情况下,它是在运行时不依赖于一些不可靠的浏览器检测(即userAgent)

小提琴示例(代码复制如下)

HTML

<div id="box"><div id="content">scroll me</div></div>
<div id="output">Scroll Left: <span id="scrollLeft"></span></div>
CSS

#box {
    width: 100px; height: 100px;
    overflow: auto;
    direction: rtl;
}
#content { width: 300px; height: 300px; }

JS

function updateScroll() {
    $('#scrollLeft').text(box.scrollLeft());
}
var box = $('#box').scroll(updateScroll);
updateScroll();

这是一个不使用浏览器检测的jQuery插件:https://github.com/othree/jquery.rtl-scroll-type

使用这个插件你可以用你自己的可预测的版本替换jQuery的scrollLeft函数,像这样:

var origScrollLeft = jQuery.fn.scrollLeft;
jQuery.fn.scrollLeft = function(i) {
    var value = origScrollLeft.apply(this, arguments);
    if (i === undefined) {
        switch(jQuery.support.rtlScrollType) {
            case "negative":
                return value + this[0].scrollWidth - this[0].clientWidth;
            case "reverse":
                return this[0].scrollWidth - value - this[0].clientWidth;
        }
    }
    return value;
};

我没有包括设置滚动偏移量的代码,但是您可以理解。

小提琴在这里:http://jsfiddle.net/scA63/

你可以试试:-

         var initialScrollLeft = $('#box').scrollLeft(), negativeToZero, startFromZero;
            if(initialScrollLeft === 0){
                startFromZero = true; 
            } else if(initialScrollLeft < 0){
                negativeToZero = true;
            } 
            var box = $('#box').scroll(function(){
                if(startFromZero){
                    if(box.scrollLeft()>0){
                        $('#scrollLeft').text(- (box.scrollLeft()));
                    }else {
                        $('#scrollLeft').text(box.scrollLeft()); 
                    }
                } else if(negativeToZero){
                    $('#scrollLeft').text(box.scrollLeft()+(box[0].scrollWidth - box[0].clientWidth));  
                } else{
                    $('#scrollLeft').text(box.scrollLeft()-(box[0].scrollWidth - box[0].clientWidth));  
                }
            });

问题:(例滚动宽度= 100)

Chrome - Most Right: 100 Most Left: 0.

IE- Most Right: 0 Most Left: 100.

Firefox -最右:0最左:-100.

解决方案# 1

正如@Lucas Trzesniewski所提到的。

你可以使用这个Jquery插件:

https://github.com/othree/jquery.rtl-scroll-type

插件用于检测浏览器正在使用的类型。将结果赋值给jQuery的支持对象rtlScrollType。您将需要元素的滚动宽度来进行转换这三种类型的值

解决方案# 2

credit: jQuery.scrollLeft()当方向是rtl -不同的浏览器有不同的值

我知道你不想为每个浏览器单独包含浏览器检测。在这个例子中,只需要为Safari和Chrome添加2行额外的代码,它就像一个魅力!

修改了它,以便更好地为您演示。

$('div.Container').scroll(function () {
    st = $("div.Container").scrollLeft() + ' ' + GetScrollLeft($("div.Container"));
    $('#scrollLeft').html(st);
});
function GetScrollLeft(elem) {
    var scrollLeft = elem.scrollLeft();
    if ($("body").css("direction").toLowerCase() == "rtl") {
        // Absolute value - gets IE and FF to return the same values
        var scrollLeft = Math.abs(scrollLeft);
        // Get Chrome and Safari to return the same value as well
        if ($.browser.webkit) {
            scrollLeft = elem[0].scrollWidth - elem[0].clientWidth - scrollLeft;
        }
    }
    return scrollLeft;
}

JSFiddle:

http://jsfiddle.net/SSZRd/1/

左边的值在所有浏览器中应该是相同的,而右边的值是在所有浏览器中不同的旧值。(在Firefox, Safari, Chrome, IE9上测试)

 1. FF-latest scrollLeft starts at 0 and goes negative when scrolling left
 2. IE 9 scrollLeft starts at 0 and goes positive when scrolling left
 3. Chrome-latest scrollLeft starts at a higher number and goes to when scrolling left
I want to avoid having code like if(ie){...}else if(ff){...}else if(chrome){...}
that would be horrible, and not maintainable in the long run in case browsers change behavior

供参考:Chrome 85(最终于2020年8月发布)修复了这个错误,并将行为与Firefox和Safari以及规范保持一致。
见https://www.chromestatus.com/feature/5759578031521792

是否有可用的特征检测?

是的,例如,使用这里提供的两张纸条中的一张(来自frac Wang):https://people.igalia.com/fwang/scrollable-elements-in-non-default-writing-modes/

在这

function scroll_coordinates_behavior_with_scrollIntoView() {
    /* Append a RTL scrollable 1px square containing two 1px-wide descendants on
       the same line, reveal each of them successively and compare their
       scrollLeft coordinates. The scrollable square has 'position: fixed' so
       that scrollIntoView() calls don't scroll the viewport. */
    document.body.insertAdjacentHTML("beforeend", "<div style='direction: rtl;'
position: fixed; left: 0; top: 0; overflow: hidden; width: 1px; height: 1px;'>'
<div style='width: 2px; height: 1px;'><div style='display: inline-block;'
width: 1px;'></div><div style='display: inline-block; width: 1px;'></div>'
3</div></div>");
    var scroller = document.body.lastElementChild;
    scroller.firstElementChild.children[0].scrollIntoView();
    var right = scroller.scrollLeft;
    scroller.firstElementChild.children[1].scrollIntoView();
    var left = scroller.scrollLeft;
    /* Per the CSSOM specification, the standard behavior is:
       - decreasing coordinates when scrolling leftward.
       - nonpositive coordinates for scroller with leftward overflow. */
    var result = { "decreasing": left < right, "nonpositive": left < 0 };
    document.body.removeChild(scroller);
    return result;
}

function scroll_coordinates_behavior_by_setting_nonpositive_scrollLeft() {
    /* Append a RTL scrollable 1px square containing a 2px-wide child and check
       the initial scrollLeft and whether it's possible to set a negative one.*/
    document.body.insertAdjacentHTML("beforeend", "<div style='direction: rtl;'
position: absolute; left: 0; top: 0; overflow: hidden; width: 1px;'
height: 1px;'><div style='width: 2px; height: 1px;'></div></div>");
    var scroller = document.body.lastElementChild;
    var initially_positive = scroller.scrollLeft > 0;
    scroller.scrollLeft = -1;
    var has_negative = scroller.scrollLeft < 0;
    /* Per the CSSOM specification, the standard behavio999r is:
       - decreasing coordinates when scrolling leftward.
       - nonpositive coordinates for scroller with leftward overflow. */
    var result = { "decreasing": has_negative ||
                   initially_positive, "nonpositive": has_negative };
    document.body.removeChild(scroller);
    return result;
}