Absolute positioning within inline elements. Is this behaviour correct?
Asked Answered
E

2

8

Consider the following simple HTML and CSS

a.rel{
  position:relative;
}
button{
  position:absolute;
  top:0;
  left:0;
}
Lorem ipsum dolor sit amet
<a class="rel" href="https://www.google.co.uk">
  <img src=
   "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png">
  <button>I'm a button</button>
</a>

Now consider CSS2 10.1.4.1

  1. If the element has 'position: absolute', the containing block is established by the nearest ancestor with a 'position' of 'absolute', 'relative' or 'fixed', in the following way:

    1. In the case that the ancestor is an inline element, the containing block is the bounding box around the padding boxes of the first and the last inline boxes generated for that element. In CSS 2.1, if the inline element is split across multiple lines, the containing block is undefined.

or CSS3 3.1.2.2

  1. If the element has position: absolute, the containing block is established by the nearest ancestor with a position other than static, in the following way:

    1. In the case that the ancestor is inline-level, the containing block depends on the direction property of the ancestor:

      1. If the direction is ltr, the top and left of the containing block are the top and left content edges of the first box generated by the ancestor, and the bottom and right are the bottom and right content edges of the last box of the ancestor.

Doesn't this mean that the button should appear on top of the image to the top left? What part of the spec have I failed to understand when the button appears below the image?

Elke answered 3/8, 2016 at 17:30 Comment(7)
@Tyler Roper: img tags don't have to be closed (and in fact cannot be closed in HTML).Clementinaclementine
@Clementinaclementine Oh, interesting. I've deleted my comment for misinformation. I had changed the <a> to display: block; previously, and added a /> to the image just to check, and it suddenly worked. I thought I was on to something, but realized I hadn't removed my display: block; on the a. Apologies, thanks for the correction :)Sarita
Sorry, but as far as understanding the relevant specs is concerned, you've kinda flunked it one step earlier already ;-) - you can not put a button into a link, that's invalid HTML. w3.org/TR/html5/text-level-semantics.html#the-a-element, w3.org/TR/html5/dom.html#interactive-content-0Heliotrope
@CBroe: While that's true, that hasn't stopped browsers from rendering a replaced inline element (the button) within an inline box (the a) in the exact same way as had the button been an img instead or some other replaced element that could legally be a child of an a.Clementinaclementine
@Clementinaclementine I'm not disputing that, and your answer explains it well. But if one bothers to be concerned what the specs say & understand it (kudos to OP, because not everyone does), I think it's at least worth a mention that one should try and do that with all relevant specs.Heliotrope
@CBroe: I'm with you on that.Clementinaclementine
Although invalid markup isn't that much of a problem in your example, I recommend replacing the button with a different element, such as another image. That eliminates any possibility of surprising or divergent browser behavior arising from invalid markup.Clementinaclementine
C
3

The height of an inline box (that is, a box generated by an element with display: inline) is determined by the strut, which is an imaginary box that's tall enough to contain text in the computed font-size. See §10.6.1 and §10.8.1, as well as the line-height propdef (although note that line-height determines the height of the line box on which the inline box appears, not the inline box itself).

Notably, an inline-level child does not affect the height of its parent inline box, even if that child is taller than the strut. The position of the button is therefore relative to the strut of the a element. This also means that the position of the button would be the same if the image were not present to begin with:

a.rel{
  position:relative;
}
button{
  position:absolute;
  top:0;
  left:0;
}
Lorem ipsum dolor sit amet
<a class="rel" href="https://www.google.co.uk">
  <!-- img src=
   "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" -->
  <button>I'm a button</button>
</a>

(The button is now horizontally closer to the text because there is no other in-flow content left in the a element besides the inter-element whitespace, which ends up being collapsed. However, its vertical position remains the same.)

Here, I've added some horizontal padding to the a element, given both it and the preceding text a background, and made the button semitransparent to show that the height of the inline box is indeed the height of the strut:

span{
  background-color:#f00;
}
a.rel{
  position:relative;
  background-color:#00f;
  padding:0 1em;
}
button{
  position:absolute;
  top:0;
  left:0;
  opacity:0.5;
}
<span>Lorem ipsum dolor sit amet</span>
<a class="rel" href="https://www.google.co.uk">
  <!-- img src=
   "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" -->
  <button>I'm a button</button>
</a>

You can see part of the inline box peeking out from the corners of the button on certain browsers; I cannot explain that beyond "different browsers render inlines differently" unfortunately.

Note also that the button isn't exactly appearing below the image. If you give the image an outline you'll see that the button isn't adhering to the bounds of the image at all (it shouldn't try to; after all, it's absolutely positioned):

a.rel{
  position:relative;
}
button{
  position:absolute;
  top:0;
  left:0;
}
img{
  outline:thin solid;
}
Lorem ipsum dolor sit amet
<a class="rel" href="https://www.google.co.uk">
  <img src=
   "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png">
  <button>I'm a button</button>
</a>
Clementinaclementine answered 3/8, 2016 at 18:0 Comment(7)
Your post hints that if you were to change the line-height of the a to 92px, it would work as the OP expected. But it doesn't.Independency
@Mr Lister: You're right. Let me know if my edit makes things clearer.Clementinaclementine
I did notice that in the various inspectors, the computed height of the a is still given as "auto", not a number in pixels.Independency
@Mr Lister: I don't know what the used value of height for an element with display: inline is, but I do know that the height property doesn't apply at all and whatever value that's set will have no effect. That's why I specifically avoided saying something like "The computed value of the height property..." or indeed, made any references to the height property.Clementinaclementine
All dat fine CSS knowledge bein' dropped, yet not one mention that button inside a would be invalid HTML ... ;-)Heliotrope
@CBroe: Because it's not relevant. Were it relevant, it would have been the very first thing I mentioned.Clementinaclementine
While not relevant here, it is still wrong. C'mon, not trying to fight you here, but you know how bad examples propagate through copy&paste.Heliotrope
B
1

The button IS on the top-left of the link. You're mistaken, however, about the size of the link. As a has display inline by default, its size doesn't increase by the size of the img, instead its just the line-height (determined by the font size).

If you increase the font-size of the link element, the button will therefore go up.

You may use the dev tools to easily view the exact dimensions of the link element.

Biauriculate answered 3/8, 2016 at 18:2 Comment(2)
Your post hints that the img is overflowing out of the a, implying that if the a had overflow:hidden, part of the img would be invisible. It isn't. Also, the dev tools all say the computed height of the link is "auto", not a value in pixels.Independency
@Mr Lister: Overflow (at least, in the context of the overflow property) only occurs when the containing block is established by a block container box. It does not occur when the containing block is established by an inline box. In other words, even though the image is exceeding the bounds of the inline box, as far as CSS is concerned it is not overflowing the inline box, because inline boxes have no concept of overflow.Clementinaclementine

© 2022 - 2024 — McMap. All rights reserved.