How can I prevent an absolutely positioned element from breaking within CSS columns?
Asked Answered
I

4

13

I have a 2 column layout using HTML and CSS. Within these columns, I have a selectmenu that will show when one of these list items are clicked. I need to position this selectmenu relative to the clicked item and not affect the layout of the elements around it, like a traditional select option menu. However, I cannot use traditional selects, as this is an autosuggest input/menu. So, I am using an absolutely positioned item for the menu container within its relatively positioned parent.

What I want: list with absolutely positioned child in columned layout

My problem is the absolutely positioned item is breaking as if it were part of the columns. This goes against everything I understand about position: absolute, but it appears it is within spec. What is more frustrating is that the browser shows the absolutely positioned item as I intend it (as long as I don’t set position: relative on the parent), but I need to set position: relative on the parent, or the selectmenu (as an absolutely positioned item) won’t always show adjoining its corresponding element.

What I get: enter image description here

I have also tried a column-like layout using using flexbox, but with this, the items are shown left to right, when I need them to be shown top to bottom, in order, as in a traditional columned layout. I could use flexbox with flex-flow: column wrap, but that would require me to know/set the height of the container element, which I cannot do. CSS columns wrap the list nicely without having to set a height.

I think the easiest solutions would be to somehow fake absolute positioning or to get flexbox to show the items in order (top to bottom) without explicitly setting a height on the container. Both of which I tried with no satisfactory results. I am open to using a JavaScript solution, but am trying my best to avoid it and solve this with CSS only.

.columns-two {
  column-count: 2;
  column-width: 300px;
  -moz-column-count: 2;
  -moz-column-width: 300px;
}
.parent {
  -webkit-column-break-inside: avoid;
  page-break-inside: avoid;
  break-inside: avoid;
  position: relative;
}
.highlight {
  background-color: #FFF8DC;
}
.absolute-element {
  width: 200px;
  position: absolute;
  background: #ffffff;
  border: 1px solid #cccccc;
  padding: 15px;
  -webkit-column-break-inside: avoid;
  page-break-inside: avoid;
  break-inside: avoid;
}
<ul class="columns-two">
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li class="parent highlight">item that shows selectmenu
    <div class="absolute-element">This is an absolute element. This is an absolute element. This is an absolute element. This is an absolute element. This is an absolute element. This is an absolute element. This is an absolute element. This is an absolute element.This is an absolute
      element. This is an absolute element. This is an absolute element. This is an absolute element.</div>
  </li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
</ul>

Here is a codepen with an example showing my problem. Remove the parent class from the HTML to see the absolutely positioned item as intended (note that this will cause the positioning to sometimes be incorrect).

Induce answered 19/7, 2016 at 16:13 Comment(1)
transform: translate3d(0,0,0); is marked as the accepted answer, but when I tested this it appears to still retain the original layout but just hides the excess that has wrapped at the right. Did you ever find a solution?Interior
I
3

I've found that using transform: translate3d(0,0,0); on the absolutely positioned element will prevent its column break.

Induce answered 17/9, 2019 at 14:48 Comment(1)
This trick is OK only if the absolute positioned element contains no links. Otherwise it fails because the mouse is still detected at the (wrong) original element position!Virtue
H
5

Use backface-visibility: hidden on the child element.

Horsehide answered 5/12, 2018 at 20:36 Comment(4)
i was dubious of this answer at first, and, after looking at the docs, i was even surer that it wouldn't work. eventually, i just went for it anyways because i had exhausted all other possible solutions, and - lo and behold - this worked like a charm! if i may ask, how did you find out about this??Polaroid
This does work in Chrome but does not correct it in Safari as of 12.1.2 --even with -webkit-backface-visibility: hidden. This answer led to me to try something else that fixes the issue, however--transform: translate3d(0,0,0); on the absolutely positioned element.Induce
@Polaroid - I found out by trial and error, like many of these silly things in CSS...Horsehide
Not working on MS Edge. OK on Firefox.Virtue
I
3

I've found that using transform: translate3d(0,0,0); on the absolutely positioned element will prevent its column break.

Induce answered 17/9, 2019 at 14:48 Comment(1)
This trick is OK only if the absolute positioned element contains no links. Otherwise it fails because the mouse is still detected at the (wrong) original element position!Virtue
S
0

Are you able to set the <li> to position: relative; with the select menu positioned absolutely inside the <li>? AFAIK this is the standard way of achieving this effect.

Then:

.selectMenu {
    position: absolute;
    top: /* height of li element */;
    left: 0;
}
Sirup answered 19/7, 2016 at 16:19 Comment(4)
In the codepen example and my actual project, the <li> is the parent element with relative positioning. Setting a top and left value as you show doesn't prevent the absolutely positioned element from breaking in the columns.Induce
Ah, apologies. I can't think of a natural solution to this, I would either use some javascript to define the correct position or abandon CSS columns.Sirup
That may be what I have to do...or just use flexbox and accept left to right ordering. But I'm still hoping there might be someone here who has had a similar issue and could offer a clever CSS solution.Induce
It seems fairly straightforward, but that's before taking into account CSS columns.. hope someone can offer a direct solution.Sirup
K
-1

Just set position:relative to ul, and position submenu if you need with property margin.

Kekkonen answered 10/1, 2017 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.