如何防止绝对定位的元素影响滚动条?

时间:2016-01-26 05:50:41

标签: html css overflow css-position

考虑一个自动提示输入,该输入位于具有一些内容和垂直滚动条的容器中:

enter image description here

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

我希望得到:

enter image description here

请注意,滚动条与上面完全相同。

但是,我得到以下内容:

enter image description here

请注意,滚动条会受到影响,建议会被删除。

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

你会如何解决这个问题?

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>

10 个答案:

答案 0 :(得分:10)

  
      
  • 当显示自动提示建议时,它们应显示在内容的顶部,而不会影响容器的滚动条。
  •   
  • 滚动容器时,输入和建议应一起移动。
  •   

仅限CSS无法完成。

要让suggestions的内容container显示在position: absolute的内容之上,而非autosuggest,则必须container,而不是父母position: relative和{{suggestions 1}})可以是suggestions

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

要使container移动滚动条,其父级之一(position: relativecontainer)必须为overflow: visible

与此相反,由于input不是autosuggest,因此会被裁剪

正如已经建议的那样,假设position: relative必须在autosuggest元素中,将position: absolute上的input更改为suggestions,以便z-index保留container滚动,可能是最好的,但需要在每个input上设置autosuggest以避免奇数重叠。

但是,如果第三方组件的提供者,...... :) ......可以被谈到suggestions可以放在{{1}之外的版本根据if content是否具有焦点,我可以使用inputinput以及它们的布局获得更多控件(仅使用CSS),...

...此示例可能是一个良好的开端(点击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>
e.preventDefault()

答案 1 :(得分:7)

这看起来有点像一种笨拙的方法并且有一个小警告,但它是纯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框添加了一些显示/隐藏,因为它看起来不错。

.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块,即使它位于绝对位置。

答案 2 :(得分:4)

你所要做的就是将.zos索引属性添加到.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

注意:将鼠标悬停在输入上并提供建议。

<强>

UPDATE2:

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

这是新答案。

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

答案 3 :(得分:2)

.autosuggest {
  position: fixed; /* add position:fixed */
}

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

答案 4 :(得分:1)

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

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

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

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

答案 5 :(得分:1)

轻松,删除

position: relative
从类.autosuggest

添加到类.container

答案 6 :(得分:1)

将此添加到您的CSS:

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

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

小提琴here

答案 7 :(得分:1)

这个怎么样:

  • 移动容器外的自动提示
  • 给出内容与输入高度相同的余量(如果你使用的是LESS / SASS,你应该可以计算出来)
  • 将autosuggest降低至占用边距空间

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

答案 8 :(得分:0)

JavaScript解决方案

我知道有人问过该解决方案不使用Javascript,但是由于所提出的解决方案都无法为我工作,因此我别无选择,只能诉诸于次优的Javascript。尽管如此,该解决方案仍然有效。

为什么上述提议的解决方案都不适合我的原因:

  1. 我正在开发一个Web组件,该组件基本上只包含原始问题中指定的输入和建议部分。
  2. 我无法假设有关Web组件的位置的任何信息 用过的(在带有滚动条的另一个元素中:自动,在带有 或不滚动,...)。

因此,我无法处理输入,建议容器以及前两个容器(相当于Web组件)之外的任何内容的CSS属性或HTML结构 ,以我为例。

我也无法将 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>

答案 9 :(得分:-1)

你只能在&#34; css&#34;风格,如果建议1,2,3元素不是&#34;输入&#34;元件。

例如:

<input>
 content
 content
</input>

suggestion1
suggestion2
suggestion3

或作为工作示例thar建议htmlnoob url in some upper mess

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