Allow absolutely positioned element to be wider than parent absolutely positioned element
Asked Answered
L

3

10

Background

I have a small one-level CSS flyout menu (well, technically it's an expanding element). It is absolutely positioned at the bottom left of a parent absolutely-positioned element that is fairly narrow. See the second h1 element below:

<div id="controls">
   <h1>Controls 1</h1>
   <h1 id="size" class="poplinks button">
      Size
      <a href="#" class="button selected" title="small"><img src=""></a>
      <a href="#" class="button" title="medium"><img src=""></a>
      <a href="#" class="button" title="large"><img src=""></a>
   </h1>
</div>

This is very simply turned into an expanding menu/flyout like so:

.poplinks:hover {
   width:auto;
}
.poplinks a {
   display:none;
}
.poplinks:hover a {
   display:inline-block;
}

Problem

This results in the following button-like element:

button

The h1 has style width:48px;, and there is also a style rule to apply width:auto; to the h1 element upon hover, so it should be able to widen. However, upon hovering, the submenu is being forced to stay no wider than the parent element's width, when I'd like it to extend to the right (out of the parent's containing box).

vertical

What I want to see (obtained by moving the element outside the parent, but I would like it to remain inside for inheriting styling and so when I move the menu bar from the left to the top, it follows automatically):

enter image description here

Is this possible? Do you have any recommendations?

See this in action for yourself in a JS Fiddle.

Browsers

Note: I plan for this to work in Firefox, Chrome, and IE 8. I am doing the main styling in Firefox & Chrome and when basically done, will add conditional CSS to get IE to work right and look as close as I can.

Rationale

The reason I am positioning the parent menu absolutely is that I'm building an application-like page for displaying images. The page will be hosted within a parent Windows application and doesn't need a lot of identifying information: just to display the desired images. I chose to make the menu absolutely positioned rather than using inline-block or floats or some other method to get my menu columns into place (there are two). However, it doesn't have to be this way. If you have a suggestion for an alternate layout or strategy, I am all ears.

Lauretta answered 12/7, 2013 at 1:14 Comment(0)
K
22

First, your #controls need overflow:visible. Then, #size should be given an explicit left instead of right. And finally, .poplinks needs white-space: nowrap to prevent the wrap.

http://jsfiddle.net/VaWeK/11/

Kreiker answered 12/7, 2013 at 7:21 Comment(3)
As a follow-up to this I posted stackoverflow.com/questions/17610991. This answer describes how to make sure the shrink-to-fit algorithm preserves whitespace, while jeff's answer points at completely avoiding shrink-to-fit.Kreiker
Sorry about the right:0 on the #size div... I switched that around while I was playing with it. I was planning to allow the control bar to be on the left or on the top and thought that using bottom:0; right:0; would pin it in the correct location in both configurations, but of course this would mess up the direction of the "popout". So I'll just set up separate stylesheets for each configuration. Thank you for the good answer! It's interesting that an absolutely-positioned element's parent overflow setting affects its sizing!Lauretta
This answer helped me with a similar issue - where I wanted an absolute child of a relative parent to be wider. white-space:nowrap on the child made it happen.Gratiana
S
4

I'm writing this answer because I might need it again in the future.

Although I've found this in the selected answer, it also mentions lots of other details asked by OP, and that somehow ended up hiding what was the important part to solve my problem.

What did the trick for me was the: white-space: nowrap.

I needed this for a dropdown component, which I think is a common use case.

The code below uses React and styled-components.

const styled = window.styled;

const LS = {};

LS.DropdownButton_DIV = styled.div`
  position: relative;
  width: 150px;
  height: 30px;
  display: flex;
  align-items: center;
  padding-left: 8px;
  border: 1px solid silver;
  user-select: none;
  cursor: pointer;
  margin-bottom: 100px;
`;

LS.Dropdown_DIV = styled.div`
  position: absolute;
  left: 0;
  top: 100%;
  display: ${props => props.open ? "block" : "none"};
`;

LS.DropdownItem_DIV = styled.div`
  display: flex;
  padding-left: 8px;
  border: 1px solid silver;
  /* THIS IS WHAT SOLVES THE PROBLEM */  
  white-space: ${props => props.noWrap ? "nowrap" : "initial"};
`;

function App() {

  const [open1,setOpen1] = React.useState(false);
  const [open2,setOpen2] = React.useState(false);

  function toggleDropdown1() {
    setOpen1((prevState) => !prevState);
  }
  
  function toggleDropdown2() {
    setOpen2((prevState) => !prevState);
  }

  return(
    <React.Fragment>
    
      <LS.DropdownButton_DIV onClick={toggleDropdown1}>
        Dropdown Button 1
        <LS.Dropdown_DIV open={open1}>
          <LS.DropdownItem_DIV>
            Dropdown Item Longer Than Parent
          </LS.DropdownItem_DIV>
        </LS.Dropdown_DIV>
      </LS.DropdownButton_DIV>
      
      <LS.DropdownButton_DIV onClick={toggleDropdown2}>
        Dropdown Button 2
        <LS.Dropdown_DIV open={open2}>
          <LS.DropdownItem_DIV noWrap={true}>
            Dropdown Item Longer Than Parent
          </LS.DropdownItem_DIV>
        </LS.Dropdown_DIV>
      </LS.DropdownButton_DIV>
      
    </React.Fragment>
  );
}

ReactDOM.render(<App/>, document.getElementById("root"));
* {
 box-sizing: border-box;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<script src="//unpkg.com/[email protected]/dist/styled-components.min.js"></script>
<div id="root"/>
Sarraute answered 24/4, 2020 at 15:11 Comment(0)
P
0

Two ways: you can either hardcode an absolutely defined width, or you can do a relative width that is greater than 100%. Width of 100% will be 100% of the containing div (as long as that div has a defined width). If this div has an absolute width, meaning it is defined with pixels instead of percentages or ems, then you can simply make the hover css wider than the parent div. For example, if your parent div is 100px wide, the hover on the child should be 200px wide or 200% or something like that.

Paleobiology answered 12/7, 2013 at 1:19 Comment(3)
Jeff, I thought about defining a width, but it sure would be nice if the child element could automatically determine its own width from its contents. However, I tried what you said--please click link and while an explicit width does indeed make it wider, the overflow is hidden instead of extending outside the parent.Lauretta
@Lauretta This is because of two reasons: you have an explicit width on the parent element of 60px, and the size button is absolutely positioned. If using absolute, the div won't widen the parent div because absolute will have no bearing on the divs surrounding it.Paleobiology
I don't want to widen the parent div. See the pic in my post.Lauretta

© 2022 - 2024 — McMap. All rights reserved.