【问题标题】:Find first scrollable parent找到第一个可滚动的父级
【发布时间】:2016-06-26 16:17:06
【问题描述】:

我有这种情况,我需要将一个元素滚动到视口中。问题是我不知道哪个元素是可滚动的。例如,在 Portrait 中,body 是可滚动的,而在 Landscape 中,它是另一个元素(还有更多情况会改变可滚动元素)

现在的问题是,给定一个需要滚动到视口中的元素,找到其第一个可滚动父级的最佳方法是什么?

我已经设置了一个演示 here。使用按钮,您可以在两种不同的情况之间切换

<div class="outer">
    <div class="inner">
        <div class="content"> 
            ...
            <span>Scroll me into view</span>
        </div>
    </div>
</div>

正文可滚动或.outer

有什么建议吗?

【问题讨论】:

    标签: javascript html css


    【解决方案1】:

    我想你想要这个。

    $('button').click(function() {
      $("body").addClass("body");
      $('.outer').toggleClass('scroller');
      check($(".content"));
    });
    
    function check(el) {
      var overflowY = el.css("overflow-y");  
      if (overflowY == "scroll") {
        alert(el.attr("class") + " has");
      } else {
        if(el.parent().length > 0)
       	  check(el.parent());
        else 
          return false;
      }
    }
    body {
      height: 450px;
      overflow-y: scroll;
    }
    
    div.inner {
      width: 200px;
      height: 400px;
      border: 1px solid #000;
    }
    
    div.outer {
      width: 200px;
      height: 200px;
    }
    
    div.outer.scroller {
      overflow-y: scroll;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <button>
      toggle
    </button>
    <div class="outer">
      <div class="inner">
        <div class="content">
          "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
          in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." adipiscing elit, sed do eiusmod tempor incididunt ut
          labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
          sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        </div>
      </div>
    </div>

    【讨论】:

      【解决方案2】:

      只需检查滚动条是否可见,如果不查看父级。

      function getScrollParent(node) {
        if (node == null) {
          return null;
        }
      
        if (node.scrollHeight > node.clientHeight) {
          return node;
        } else {
          return getScrollParent(node.parentNode);
        }
      }
      

      【讨论】:

      • 如果节点有一个inline 父节点,clientHeight 将为 0,并在此处返回。最好忽略 clientHeight 为 0 的节点。
      • 这很好,但不适用于溢出但仍无法滚动的节点。如果您为overflowY value 添加检查,它似乎运行良好。 let overflowY = window.getComputedStyle(node).overflowY;let isScrollable = overflowY !== 'visible' &amp;&amp; overflowY !== 'hidden';if (isScrollable &amp;&amp; node.scrollHeight &gt; node.clientHeight) {
      • 根据@RustyToms 和 OP,我重写了这个:gist.github.com/twxia/bb20843c495a49644be6ea3804c0d775
      • 又名while (node &amp;&amp; node.scrollHeight &lt;= node.clientHeight) node = node.parentNode;
      【解决方案3】:

      如果您使用的是 jQuery UI,您可以使用 scrollParent 方法。看看APIsource

      来自 API:

      .scrollParent():获取最近的可滚动祖先元素

      此方法不接受任何参数。这种方法 找到最近的允许滚动的祖先。换句话说, .scrollParent() 方法找到当前选中的元素 元素将在其中滚动。

      注意:此方法仅适用于包含一个元素的 jQuery 对象。

      如果您不使用 jQuery UI,而是使用 jQuery,那么还有提供类似功能的替代独立库,例如:

      jquery-scrollparent

      【讨论】:

      【解决方案4】:

      这是 jQuery UI scrollParent 方法的纯 JS 端口,cweston spoke of。我选择了这个而不是接受的答案的解决方案,如果还没有内容溢出,它将找不到滚动父级。

      与我的端口的一个区别是,如果没有找到具有正确值的 CSS overflow 属性的父级,我将返回 &lt;body&gt; 元素。 JQuery UI,而是返回 document 对象。这很奇怪,因为像 .scrollTop 这样的值可以从 &lt;body&gt; 中检索到,但不能从 document 检索到。

      function getScrollParent(element, includeHidden) {
          var style = getComputedStyle(element);
          var excludeStaticParent = style.position === "absolute";
          var overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
      
          if (style.position === "fixed") return document.body;
          for (var parent = element; (parent = parent.parentElement);) {
              style = getComputedStyle(parent);
              if (excludeStaticParent && style.position === "static") {
                  continue;
              }
              if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent;
          }
      
          return document.body;
      }
      

      【讨论】:

      • 也许我遗漏了一些东西,但它对我来说是倒退的...... .scrollTop 可以从文档中读取:document.documentElement.scrollTopdocument.body.scrollTop 返回0 而不管实际情况如何滚动位置(您可以在此处的 devtools 中进行测试)。这意味着 Jquery UI 返回文档而不是正文是有意义的。
      • 与 jQuery 相同:$("body").scrollTop() 始终返回 0$(document).scrollTop() 返回您的实际滚动位置。
      • 返回document.scrollingElement 以解决@Skeets 点可能更好。也许这仍然因浏览器而异?
      【解决方案5】:

      票数最多的答案并非在所有情况下都有效scrollHeight &gt; clientHeight 可以是true,即使没有滚动条。

      我找到了这个要点解决方案https://github.com/olahol/scrollparent.js/blob/master/scrollparent.js#L13

      ^ 总功劳归于编写代码的https://github.com/olahol

      重构为es6:

      export const getScrollParent = (node) => {
        const regex = /(auto|scroll)/;
        const parents = (_node, ps) => {
          if (_node.parentNode === null) { return ps; }
          return parents(_node.parentNode, ps.concat([_node]));
        };
      
        const style = (_node, prop) => getComputedStyle(_node, null).getPropertyValue(prop);
        const overflow = _node => style(_node, 'overflow') + style(_node, 'overflow-y') + style(_node, 'overflow-x');
        const scroll = _node => regex.test(overflow(_node));
      
        /* eslint-disable consistent-return */
        const scrollParent = (_node) => {
          if (!(_node instanceof HTMLElement || _node instanceof SVGElement)) {
            return;
          }
      
          const ps = parents(_node.parentNode, []);
      
          for (let i = 0; i < ps.length; i += 1) {
            if (scroll(ps[i])) {
              return ps[i];
            }
          }
      
          return document.scrollingElement || document.documentElement;
        };
      
        return scrollParent(node);
        /* eslint-enable consistent-return */
      };
      

      你可以像这样使用它:

      const $yourElement = document.querySelector('.your-class-or-selector');
      getScrollParent($yourElement);
      

      【讨论】:

      • 您应该直接链接到您所引用的答案,而不是说“获得最多票数的答案”,因为票数会随着时间而变化。
      【解决方案6】:

      @Web_Designer's answer 的基础上进一步发展,

      如果您为该元素传递 jQuery object 并收到以下错误,

      Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'
      

      然后尝试仅传递 Dom Node element,如果该元素是单个元素,则 btw 位于数组键 0 处。例如。

      getScrollParent(jQuery("#" + formid)[0])
      

      【讨论】:

        【解决方案7】:

        使用 google chrome 开发工具,当您向下滚动页面的一部分时,检查页面,选择您认为可能正在滚动的 DOM 节点。然后拉起控制台(在开发工具的 Elements 选项卡中按 ESC)并输入 $0.scrollTop。这将打印出该元素的当前滚动位置。如果它不是 0,那么您将知道这是正在滚动的元素。

        【讨论】:

        • 扩展您的评论,以下代码片段将查询所有已滚动的元素。 Array.prototype.slice.call(document.querySelectorAll('*')).filter(el =&gt; el.scrollTop &gt; 0)
        【解决方案8】:

        这是找到你想要的东西的方法。

        document.addEventListener('scroll', (e) => {
            console.log(e.target.scrollingElement.scrollTop);
        })
        

        【讨论】:

          【解决方案9】:

          这是我目前所拥有的,似乎可以跨 Web 组件自定义元素 shadowRoots 工作;一旦滚动开始卸载此处理并跟踪特定节点的滚动事件,我们可能会进一步扩展它以添加滚动事件处理程序

          function scroller(node){ return node.scrollTop }
          function handler(event){
                  const path = event.composedPath();
                  const scrollNode = path.find(scroller);
                  const scrollNodes = path.filter(scroller);
                  console.warn('scroll!',{scrollNode, scrollNodes});
          }
          window.addEventListener('mousewheel', handler, {capture: true});
          window.addEventListener('keydown', handler, {capture: true});
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-12-26
            • 2016-08-22
            • 2022-01-07
            • 1970-01-01
            • 2017-01-13
            • 1970-01-01
            • 2012-01-22
            相关资源
            最近更新 更多