【问题标题】:How to prevent an absolutely positioned element to affect the scrollbar?如何防止绝对定位的元素影响滚动条?
【发布时间】:2016-05-02 15:44:06
【问题描述】:

考虑一个位于包含一些内容和垂直滚动条的容器中的自动建议输入:

显示自动建议时,它们应该出现在内容的顶部不会影响容器的滚动条

我希望得到:

注意滚动条和上面一模一样。

但是,我得到以下信息:

注意滚动条受到影响,建议被剪切。

为什么绝对定位的建议会影响容器的滚动条?

你会如何解决这个问题?

Playground here

注意事项:

  • 滚动容器时,输入和建议应该一起移动。
  • 您可以修改 HTML,但输入和建议列表应保留在 .autosuggest 内(将 .autosuggest 视为第三方组件,其 HTML 无法更改,但您可以更改其 CSS)。李>
  • 如果有帮助,您可以使用 flexbox。
  • 我正在寻找纯 CSS 的解决方案。请不要使用 Javascript。

.container {
  height: 100px;
  width: 300px;
  margin-top: 50px;
  overflow-y: auto;
  border: 1px solid black;
}
.autosuggest {
  position: relative;
  width: 250px;
}
.input {
  font-size: 16px;
  width: 230px;
  padding: 5px 10px;
  border: 0;
  background-color: #FFEBBF;
}
.suggestions {
  list-style-type: none;
  margin: 0;
  padding: 5px 10px;
  width: 230px;
  background-color: #85DDFF;
  position: absolute;
}
.content {
  width: 120px;
  padding: 5px 10px;
}
<div class="container">
  <div class="autosuggest">
    <input class="input" type="text" value="input">
  </div>
  <div class="content">
    content content content content content content content content content content
  </div>
</div>

<div class="container">
  <div class="autosuggest">
    <input class="input" type="text" value="input">
    <ul class="suggestions">
      <li>suggestion 1</li>
      <li>suggestion 2</li>
      <li>suggestion 3</li>
      <li>suggestion 4</li>
      <li>suggestion 5</li>
      <li>suggestion 6</li>
      <li>suggestion 7</li>
      <li>suggestion 8</li>
      <li>suggestion 9</li>
    </ul>
  </div>
  <div class="content">
    content content content content content content content content content content
  </div>
</div>

【问题讨论】:

  • 预期结果是什么?容器溢出-y 设置为自动并具有固定高度。自动建议不能让该容器保持原样。删除 overlow 并且滚动消失
  • @charlietfl 我用预期的行为更新了问题。
  • 你也可以用代码更新你的问题吗?屏幕截图很好,但您现在应该知道您需要在问题中包含代码。
  • @BoltClock 我在问题中添加了一个代码 sn-p。
  • 请问让建议随输入一起滚动的目的是什么? ...不能向下滚动以查看内容,因为建议已经部分隐藏了。

标签: html css overflow css-position


【解决方案1】:

您所要做的就是将 z-index 属性添加到 .autosuggest 和 .suggestions 这里是来自 codepen 的代码示例

http://codepen.io/HTMLNoob/pen/EPEXKN

.autosuggest {
  z-index: -10;
  width: 250px;
}
.suggestions {
  list-style-type: none;
  margin: 0;
  padding: 5px 10px;
  width: 230px;
  background-color: #85DDFF;
  position: absolute;
  z-index: 10;
}

更新:

我相信这就是你要找的东西

http://jsbin.com/fegibaboyo/1/edit?html,css,output

注意:将鼠标悬停在带有建议的输入上。

更新2:

http://jsbin.com/mojopuboku/edit?html,css,output

这是新的答案。

我已经在大多数浏览器(Firefox、Chrome、Opera)中测试了这个答案,所有这些都执行相同的结果。(我不确定 Safari,因为我无法在我的 Windows XP 上安装 Safari)

【讨论】:

  • 尝试发布链接应该会导致错误。它告诉你什么?
  • “您必须提供带有任何链接的代码.....”我这样做了,它只是发布代码而不是链接
  • 你确定你已经包含了代码吗?除了链接(在代码块中),我什么也没看到。
  • @HTMLNoob 问题标题明确提到了滚动条。在您的解决方案中,容器似乎没有滚动条。你是对的,建议应该始终附加到输入中,但它们的外观不应影响容器的滚动条位置。
  • @HTMLNoob 您错过了问题中的注释,即输入建议应该一起移动。
【解决方案2】:

为什么不直接将 ul 和 li 元素从 div 容器中拉出,然后通过设置负的顶部位置来上移建议?

http://jsbin.com/ficalu/edit?html,css,output

更新: 将 .autosuggest 更改为 position: absolute

http://jsbin.com/gezimi/edit?html,css,output

【讨论】:

  • 我可能应该更具体一点,说.autosuggest 是第三方组件,所以你不能将.suggestions 移到.autosuggest 之外。
  • 知道了。如何将 .autosuggest 更改为位置:absolute jsbin.com/gezimi/edit?html,css,output
  • 请重新检查链接。我假设您粘贴了错误的链接,或者在那之后更新了 Bin。好像没有解决问题。
  • 这里尝试将position: absolute 用于.autosuggestjsbin.com/yalokodajo/1/edit?html,css,output 但它不起作用,因为现在滚动容器会使输入始终位于顶部(它应该像在问题中的原始链接)。
【解决方案3】:

简单,删除

position: relative

来自 .autosuggest 类并添加到 .container 类

【讨论】:

  • .container { 高度:100px;宽度:300px;边距顶部:50px;边框:1px纯黑色;位置:相对; } .autosuggest { 宽度:300px;溢出-y:滚动;高度:100px; } .input { 字体大小:16px;宽度:230px;填充:5px 10px;边框:0;背景颜色:#FFEBBF; } .suggestions { 列表样式类型:无;边距:0;填充:5px 10px;宽度:230px;背景颜色:#85DDFF;位置:绝对; } .content { 宽度:120px;填充:5px 10px; }
  • 你的 JSbin 没有按预期工作(它根本没有滚动条,内容似乎在容器之外)。
  • 从你的问题来看,我认为这是你想要的结果 - 不确定我当时是否理解这个问题?
  • Class .suggestions 可以添加一个 max-height 和一个 overflow-y,但这会在 .suggestions UL 中添加一个滚动条。你不能在它的父级上有一个滚动条来影响孩子的溢出位置,滚动必须在你溢出的内容上,否则整个面板只会移动而不是滚动。
【解决方案4】:
.autosuggest {
  position: fixed; /* add position:fixed */
}

.suggestions {
 /* remove position:absolute */
}

【讨论】:

    【解决方案5】:
    • 显示自动建议时,它们应显示在内容顶部,而不会影响容器的滚动条。
    • 滚动容器时,输入和建议应该一起移动。

    仅使用 CSS 无法做到这一点。

    要让suggestions 出现在container 的内容之上,不被剪裁,它必须有position: absolute,并且它的父项(autosuggestcontainer)不能是position: relative

    缺点是suggestions 不会在滚动时移动。

    要使suggestions 随滚动移动,其父级之一(autosuggestcontainer)必须是position: relative

    这样做的缺点是,由于container 不是overflow: visible,它将被剪裁


    如前所述,假设input 必须在autosuggest 元素内,将autosuggest 上的position: relative 更改为position: absolute,因此input 在滚动时与suggestions 保持一致, 可能是最好的,但需要在每个 container 上设置 z-index 以避免奇怪的重叠。

    但是如果第三方组件的提供者,... :) ...,可以将input 放在autosuggest 元素之外的版本中,根据input 是否具有焦点,可以仅使用 CSS 对 suggestionscontent 及其布局进行更多控制...

    ...这个示例可能是一个好的开始(点击input 显示suggestions)。

    .container {
      background-color: white;
      width: 300px;
      min-height: 100px;
      margin-top: 50px;
      border: 1px solid black;
      overflow: auto;
      position: relative;
    }
    .autosuggest {
      position: relative;
      width: 250px;
      z-index: 1;
    }
    .input {
      font-size: 16px;
      width: 245px;
      padding: 5px 10px;
      border: 0;
      background-color: #FFEBBF;
      position: relative;
      z-index: 1;
    }
    .suggestions {
      list-style-type: none;
      margin: 0;
      padding: 5px 10px;
      width: 245px;
      background-color: #85DDFF;
      display: none;
    }
    .content {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      padding: 35px 10px 5px 10px;
      overflow: auto;
      z-index: 0;
    }
    input:focus ~ .autosuggest .suggestions {
      display: block;
    }
    <div class="container">
      <input class="input" type="text" value="input">
      <div class="autosuggest">
        <ul class="suggestions">
          <li>suggestion 1</li>
          <li>suggestion 2</li>
          <li>suggestion 3</li>
          <li>suggestion 4</li>
          <li>suggestion 5</li>
          <li>suggestion 6</li>
          <li>suggestion 7</li>
          <li>suggestion 8</li>
          <li>suggestion 9</li>
        </ul>
      </div>
      <div class="content">
        content content content content content content content content content content
        content content content content content content content content content content
        content content content content content content content content content content
        content content content content content content content content content content
        content content content content content content content content content content
        content content content content content content content content content content
      </div>
    </div>
    
    <div class="container">
      <input class="input" type="text" value="input">
      <div class="autosuggest">
        <ul class="suggestions">
          <li>suggestion 1</li>
          <li>suggestion 2</li>
          <li>suggestion 3</li>
          <li>suggestion 4</li>
          <li>suggestion 5</li>
        </ul>
      </div>
      <div class="content">
        content content content content content content content content content content
        content content content content content content content content content content
        content content content content content content content content content content
      </div>
    </div>

    【讨论】:

    • 很好的解释。为我工作!!!
    【解决方案6】:

    将此添加到您的 CSS 中:

    .input:focus + .suggestions {
      position: fixed;
      display: block;
    }
    

    并在您的 CSS 中将 display: none; 添加到 .suggestions

    小提琴here.

    【讨论】:

    • 当你滚动时,建议不会在你的小提琴中移动。
    • 你想让它们也被滚动吗?
    【解决方案7】:

    只有当建议的 1,2,3 元素不是“input”元素的子元素时,才能以“css only”样式制作。

    例如:

    <input>
     content
     content
    </input>
    
    suggestion1
    suggestion2
    suggestion3
    

    或作为工作示例,建议使用 htmlnoob url in some upper mess

    其他修复,只能在旧浏览器中使用,例如 ie5。

    【讨论】:

    • 对不起,我不明白你的提议。您可以在问题中清楚地看到内容不是输入的孩子。
    【解决方案8】:

    这个怎么样:

    • 将自动建议移到容器外
    • 为内容提供与输入高度相同的边距(如果您使用 LESS/SASS,您应该能够计算出来)
    • 放下自动建议以占据边距空间

    我对 CSS 所做的更改旁边有注释。请注意,这仅在 Chrome 中进行了测试。您也可以稍微优化 HTML,例如删除示例中的容器,但其中可能有多个元素。

    HTML:

      <div class="autosuggest">
        <input class="input" type="text" value="input">
        <ul class="suggestions">
          <li>suggestion 1</li>
          <li>suggestion 2</li>
          <li>suggestion 3</li>
          <li>suggestion 4</li>
          <li>suggestion 5</li>
          <li>suggestion 6</li>
          <li>suggestion 7</li>
          <li>suggestion 8</li>
          <li>suggestion 9</li>
        </ul>
      </div>
    
      <div class="container">
        <div class="content">
          content content content content content content content content content content
        </div>
      </div>
    

    CSS:

    .container {
      height: 100px;
      width: 300px;
      overflow-y: auto;
      border: 1px solid black;
    }
    .autosuggest {
      position: relative;
      top:29px; /* height of input */
      left:1px; /* width of container border */
      width: 250px;
    }
    .input {
      font-size: 16px;
      width: 230px;
      padding: 5px 10px;
      border: 0;
      background-color: #FFEBBF;
    }
    .suggestions {
      list-style-type: none;
      margin: 0;
      padding: 5px 10px;
      width: 230px;
      background-color: #85DDFF;
      position: absolute;
    }
    .content {
      width: 120px;
      margin-top: 29px; /* height of input */
      padding: 5px 10px;
    }
    

    【讨论】:

    • 您的解决方案不满足“滚动容器时,输入和建议应该一起移动。”
    • 啊,我看错了那部分,让我想想另一种解决方案
    • 是的,所以有一个想法。我认为如果没有 JS,您将无法满足您的所有要求。如果你想让它保持纯 CSS,你将不得不妥协。考虑不滚动输入+建议(根据我的回答)或隐藏关于滚动/散焦的建议(根据 HTML noobs 回答)
    【解决方案9】:

    这似乎有点笨拙,并且有一个小警告,但它是纯 CSS/没有 HTML 结构修改。

    基本上,我将 .container 设为主要父级,而不是尝试从较低级别 (.autosuggest) 工作。一步一步:

    • position: relative 移动到.container
    • 使.autosuggest 绝对定位(顶部/左侧默认为0px)。
      • 为其设置更高的 z-index,使其始终位于顶部
    • 使.content 绝对定位所有四个边0px,因此它的大小与.container 相同
    • 将溢出滚动条移动到.content div
    • (这是警告)将.content 的顶部填充设置为.input 的高度+ 实际所需的填充。否则 .content 在输入元素后面。

    你最终会得到这个:

        .container {
          height: 100px;
          width: 300px;
          margin-top: 50px;
          border: 1px solid black;
          position: relative;
        }
        .autosuggest {
          width: 250px;
          position: absolute;
          z-index: 50;
        }
        .input {
          font-size: 16px;
          width: 230px;
          padding: 5px 10px;
          border: 0;
          background-color: #FFEBBF;
        }
        .autosuggest .input:focus ~ .suggestions{
          display: block;
        }
        .suggestions {
          display: none;
          list-style-type: none;
          margin: 0;
          padding: 5px 10px;
          width: 230px;
          background-color: #85DDFF;
        }
        .content {
          overflow-y: auto;
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          padding: 28px 10px 5px;
        }
    <div class="container">
      <div class="autosuggest">
        <input class="input" type="text" value="input">
        <ul class="suggestions">
          <li>suggestion 1</li>
          <li>suggestion 2</li>
          <li>suggestion 3</li>
          <li>suggestion 4</li>
          <li>suggestion 5</li>
          <li>suggestion 6</li>
          <li>suggestion 7</li>
          <li>suggestion 8</li>
          <li>suggestion 9</li>
        </ul>
      </div>
      <div class="content">
        content content<br >content content content content content<br ><br ><br ><br ><br ><br >content content content
      </div>
    </div>

    我还为自动建议框添加了一些显示/隐藏,因为它看起来不错。

    .autosuggest .input:focus ~ .suggestions{
      display: block;
    }
    .suggestions {
      display: none;
    }
    

    Working jsFiddle.

    经过测试的 FF 43、Chrome 47


    滚动容器时,输入和建议应该一起移动。

    这很可能需要 JavaScript。根据定义,将建议设置为绝对位置会将它们从内容流的其余部分中删除。您可以在 .suggestions div 上设置一个高度并单独滚动它,但如果绝对定位(同样,仅使用 CSS),则滚动不能(据我所知)绑定到 .content 滚动。

    类似:

    $('.content').on('scroll', function () {
      $('.autosuggest .suggestions').scrollTop($(this).scrollTop());
    });
    

    为什么绝对定位的建议会影响容器的滚动条?

    从技术上讲,绝对定位的元素不会影响父元素的高度。但是,它们仍然被视为元素的内容 - 溢出的内容(至少在这种情况下)。

    overflow 属性指定当内容溢出其块级容器时是剪切内容、呈现滚动条还是仅显示内容。 (from MDN)

    由于.container 已将溢出设置为滚动,它会显示一个滚动条,因为它的一个子项溢出了边界框。如果您将溢出更改为隐藏,它将隐藏所有扩展内容并设置为可见,您将看到.autosuggest 扩展超过.container,但.content 也是如此(因为它也是扩展边界框的内容)。

    您会看到一个滚动条,因为 .suggest 内容在视觉上超出了 .container 块,即使它是绝对定位的。

    【讨论】:

      【解决方案10】:

      Javascript 解决方案

      我知道有人要求解决方案没有使用 Javascript,但由于 没有一个提议的解决方案适合我,我别无选择,只能求助于非常次优的 Javascript尽管如此,解决方案仍然有效。

      上述建议的解决方案都不适合我的原因

      1. 我正在开发一个 Web 组件,它基本上只包含原始问题中指定的输入和建议部分。
      2. 我无法假设 Web 组件可能在哪里 使用(在另一个元素内滚动:自动,在弹出窗口内 或没有滚动,...)。

      因此,我无法使用输入、建议容器和前 2 个容器之外的任何内容的 CSS 属性或 HTML 结构,这将等同于 Web 组件,就我而言。

      正如一些解决方案所建议的那样,我也无法将 position: absolute 应用于容器(因为原因 #2)。

      这一切都表明,我只是将这里留给那些出于某种原因不能接受任何其他解决方案并且别无选择只能求助于 Javascript 的人。我希望这会对他们有所帮助。

      const header = document.querySelector('.child-header');
      const content = document.querySelector('.child-content');
      
      const open = (el) => {
        if (!content.hasAttribute('open')) {
          content.toggleAttribute('open');
        }
      }
      
      const close = (el) => {
        if (content.hasAttribute('open')) {
          content.toggleAttribute('open');
        }
      }
      
      window.addEventListener('click', (e) => {
        let element = e.target;
        let notInsideContent = true;
        while (element && notInsideContent) {
          notInsideContent = !element.classList.contains('child-content');
          element = element.parentElement;
        }
      
        if (notInsideContent) {
          close(content);
        }
      });
      
      const repositionContent = () => {
        const headerStyle = header.getBoundingClientRect();
        content.style.left = `${headerStyle.x}px`;
        const contentTop = headerStyle.y + headerStyle.height;
        content.style.top = `${contentTop}px`;
        content.style.width = `${headerStyle.width}px`;
      
        // Make sure height takes into account bottom of page scroll
        const windowBottom = window.innerHeight;
        const contentMaxHeight = contentTop + content.scrollHeight;
        if (contentMaxHeight > windowBottom) {
          content.style.maxHeight = `${
            content.scrollHeight -
            (contentMaxHeight - windowBottom)
          }px`;
        } else {
          content.style.maxHeight = '';
        }
      };
      
      header.addEventListener('click', (e) => {
        e.stopPropagation();
        open(content);
        repositionContent();
      });
      
      // Listen to ANY scroll event on the document
      document.addEventListener('scroll', e => {
        if (!content.hasAttribute('open')) {
          return;
        }
      
        // TODO Check scroll provoked position change for content element
      
        // Check scroll origin element doesn't come from within
        const path = e.composedPath();
        let element = path.pop();
        let notInsideContentElement = true;
        while (element && notInsideContentElement) {
          notInsideContentElement = (element !== content);
          element = path.pop();
        }
      
        if (notInsideContentElement) {
          repositionContent();
        }
      }, true);
      .scrollable {
        overflow: auto;
        border: 1px solid black;
      }
      
      .child-header {
        border: 1px solid red;
        box-sizing: border-box;
        cursor: pointer;
      }
      
      .child-content[open] {
        display: block;
      }
      
      .child-content {
        display: none;
        border: 1px solid blue;
        box-sizing: border-box;
        background: blue;
        color: white;
        position: fixed;
        overflow-y: auto;
      }
      <div class="scrollable">
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div class="parent">
          <div class="child-header">
            Click me for suggestions
          </div>
          <div class="child-content">
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
            <div>Suggestion</div>
          </div>
        </div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
        <div>Element</div>
      </div>

      【讨论】:

        猜你喜欢
        • 2015-05-28
        • 2017-02-08
        • 1970-01-01
        • 2016-10-18
        • 1970-01-01
        • 1970-01-01
        • 2019-03-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多