Absolute position element's width truncates when extending beyond relative parent's right border
Asked Answered
F

2

14

These position: absolute; divs placed inside/around a position: relative; parent receive height/width from the size and length of the contained text.

However, if one of them extends beyond the right border of the relative parent, its width appears to truncate.

How can I make the div which extends beyond the right side of the relative parent behave as the others which don't, using only css? (Should work on all major browsers including Edge but not IE)

Here is a fiddle, but I will also copy the code + screenshot below:

example

HTML:

<div id="relative">
    <div id="absolute-working-left">
        My width and height are determined by my text content.
    </div>
    <div id="absolute-working-middle">
        Mine, too.
    </div>
    <div id="absolute-problem">
        But not me... I am a problem, because my width is truncated when I extend beyond the right side of my 'relative' parent.  WHY???
    </div>
</div>

...and the styles. Note that I want the absolute div width to reach a max of 200px before wrapping. Otherwise, the width should be determined by the length of the contained text:

#relative {
  position: relative;
  width: 100px;
  height: 100px;
  margin-left: 300px;
  background-color: white;
}

#absolute-problem,
#absolute-working-left,
#absolute-working-middle {
  position: absolute;
  top: 45px;
  font-size: 12px;
  max-width: 200px;
}

#absolute-working-left {
  background-color: lightblue;
  left: -300px;
}
#absolute-working-middle {
  background-color: lightgreen;
}
#absolute-problem {
  background-color: red;
  left: 80px;
}

This issue arose while implementing a tooltip which, when used by a developer, needs to position itself WRT an offsetParent (or the body, if no preceding offsetParent exists in its DOM branch).

Edit: How can I achieve the desired behavior using only css?

The solution needs to work on all major browsers including Edge, but not IE. And just to reiterate, the width of the absolute divs' cannot be predicted as it should be determined by the length of the texts... the only exception being that there will be a max-width (in this example, it is 200px).

Fauve answered 1/2, 2017 at 5:49 Comment(2)
Check this Fiddle. Is it what you wants?Tetanize
@MuhammadUsman Unfortunately I can't predict the width of the text content, so hard-coding a width into the styles won't work.Fauve
N
11

The #absolute-problem div has a left set to a particular value, an auto width and an auto right. According to this rule in §10.3.7 of the spec, this would shrink the div's width to fit its contents.

'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'.

While there doesn't seem to be a reliable way to achieve the exact desired behavior (as there aren't enough properties set to calculate the width), following are some ways to work around this problem.

Set a width for the absolute div

A trivial solution would be to set its width, so that it doesn't shrink.

#absolute-problem {
  background-color: red;
  left: 80px;
  width: 100px;
}

body {
  background: #EEE;
}
#relative {
  position: relative;
  width: 100px;
  height: 100px;
  margin-left: 300px;
  background-color: white;
  font-size: 12px;
}
#absolute-problem,
#absolute-working-left,
#absolute-working-middle {
  position: absolute;
  top: 45px;
  font-size: 12px;
  max-width: 200px;
}
#absolute-working-left {
  background-color: lightblue;
  left: -300px;
}
#absolute-working-middle {
  background-color: lightgreen;
}
#absolute-problem {
  background-color: red;
  left: 80px;
  width: 100px;
}
<div id="relative">
  relative parent
  <div id="absolute-working-left">
    My width and height are determined by my text content.
  </div>
  <div id="absolute-working-middle">
    Mine, too.
  </div>
  <div id="absolute-problem">But not me... I am a problem, because my width is truncated when I extend beyond the right side of my 'relative' parent. WHY???</div>
</div>

Set a right for the absolute div

It the width of your div is unknown, one way to get around this is to set the right as well. This adjusts the width of your div accordingly.

#absolute-problem {
  background-color: red;
  right: -80px;
  left: 80px;
}

body {
  background: #EEE;
}
#relative {
  position: relative;
  width: 100px;
  height: 100px;
  margin-left: 300px;
  background-color: white;
  font-size: 12px;
}
#absolute-problem,
#absolute-working-left,
#absolute-working-middle {
  position: absolute;
  top: 45px;
  font-size: 12px;
  max-width: 200px;
}
#absolute-working-left {
  background-color: lightblue;
  left: -300px;
}
#absolute-working-middle {
  background-color: lightgreen;
}
#absolute-problem {
  background-color: red;
  left: 80px;
  right: -80px;
}
<div id="relative">
  relative parent
  <div id="absolute-working-left">
    My width and height are determined by my text content.
  </div>
  <div id="absolute-working-middle">
    Mine, too.
  </div>
  <div id="absolute-problem">But not me... I am a problem, because my width is truncated when I extend beyond the right side of my 'relative' parent. WHY???</div>
</div>

Set the width to fit-content/max-content

Another way would be to use a fit-content or max-content (limited browser compatibility) for the absolute div, instead of setting its right. This helps if the content of your div doesn't necessarily extend to the maximum width.

#absolute-problem {
  background-color: red;
  right: -80px;
  width: fit-content;
}

body {
  background: #EEE;
}
#relative {
  position: relative;
  width: 100px;
  height: 100px;
  margin-left: 300px;
  background-color: white;
  font-size: 12px;
}
#absolute-problem,
#absolute-working-left,
#absolute-working-middle {
  position: absolute;
  top: 45px;
  font-size: 12px;
  max-width: 200px;
}
#absolute-working-left {
  background-color: lightblue;
  left: -300px;
}
#absolute-working-middle {
  background-color: lightgreen;
}
#absolute-problem {
  background-color: red;
  left: 80px;
  width: fit-content;
}
<div id="relative">
  relative parent
  <div id="absolute-working-left">
    My width and height are determined by my text content.
  </div>
  <div id="absolute-working-middle">
    Mine, too.
  </div>
  <div id="absolute-problem">But not me...</div>
</div>

Set min-width and max-width

A realistic approach would be to give the the ability to adjust its size within a given range, in order to keep its overflow in check.

#absolute-problem {
  background-color: red;
  left: 80px;
  min-width: 50px;
  max-width: 200px;
}

body {
  background: #EEE;
}
#relative {
  position: relative;
  width: 100px;
  height: 100px;
  margin-left: 300px;
  background-color: white;
  font-size: 12px;
}
#absolute-problem,
#absolute-working-left,
#absolute-working-middle {
  position: absolute;
  top: 45px;
  font-size: 12px;
  max-width: 200px;
}
#absolute-working-left {
  background-color: lightblue;
  left: -300px;
}
#absolute-working-middle {
  background-color: lightgreen;
}
#absolute-problem {
  background-color: red;
  left: 80px;
  min-width: 50px;
  max-width: 200px;
}
<div id="relative">
  relative parent
  <div id="absolute-working-left">
    My width and height are determined by my text content.
  </div>
  <div id="absolute-working-middle">
    Mine, too.
  </div>
  <div id="absolute-problem">But not me...</div>
</div>
Natatory answered 1/2, 2017 at 6:16 Comment(12)
This is correct. See rule #3 in §10.3.7 of the spec. This is done to prevent #absolute-problem from overflowing its containing block wherever possible.Degrading
Thank you for this! Makes sense, but I'm still not quite sure how to solve my problem... I don't think I can predict the proper right value. In the proposed solution, setting right: -80px results in a div whose text wraps at a width of 100px. However the intended behavior is for the wrap to happen at the max width:200px. If I were sure that I would always have wrapping text, then I could set right to some arbitrarily large value to ensure max-width were always respected. But in the case of text which does not reach the max-width, the containing div would be much too long.Fauve
@Manningham: That's the problem with shrink-to-fit width, and it applies in every situation where shrink-to-fit is used (including inline-blocks, floats, etc). There doesn't seem to be a reliable way to achieve your desired behavior.Degrading
I believe one way would be to use width: fit-content on your absolute div.Natatory
Hmm @JohnBupit just plugged that into the fiddle but didn't seem to do the trick... will investigate more though.Fauve
@Degrading Thanks for your answer, if I can't find a solution to this by tomorrow evening I'll check it.Fauve
@Fauve Seems like Firefox and Chrome seem to handle fit-content differently.Natatory
@Fauve One solution that could help would be to use max-content for a width. (See the linked MDN page). Make sure to use both the prefixed and the unprefixed versions. Updated fiddle. The disadvantage of the method is that it doesn't work in IE and Edge.Gati
@MrLister Oh, such a bummer about the compatibility issue! Works very nicely in chrome, but I need to support all major browsers except IE. Will update the question with that info.Fauve
@JohnBupit I added a more concrete question to the top and bottom edit field of the post. Could you put a line at the top of your answer saying something like 'You cannot achieve the desired behavior using only css' at the top of your answer? The three solutions you have listed are good for reference/understanding but do not solve the problem (which I just clarified with an edit, sorry not to be clear in the beginning) either because of browser compatibility issues or because of the constraints of the problem.Fauve
@MrLister I just fired up MS Edge in virtual box and width: max-content; seems to work! Will investigate further this afternoon.Fauve
@MrLister Edit: Nevermind, I accidentally ran the wrong fiddle... max-content does not work in MS Edge.Fauve
B
6

I met this problem in one of my projects too and I found a simple solution to this. Just add a negative margin-right to the box.

#absolute-problem {
  margin-right: -9999999px;
}

This should work in that situation. The 9999999px is very high so that the box will extend to the right as long as the content is. If you want a limit, give it a reasonable length will work.

Barncard answered 15/3, 2021 at 5:53 Comment(2)
This works excellent! None of the options in the accepted answers worked for me, but this definitely solves my issue.Propane
A pedant would probably argue its compatibility in an Ultra Max HD IMAX Pro+ Max screen whose width >10000000px. For the rest of us mortals, it works perfectly well !Natatory

© 2022 - 2024 — McMap. All rights reserved.