1

I have this container with some text and an image inside it as last element, I am trying to set an ::after to my container, however it is being shown over the image and not as the last element of the container:

I understand that I can't set a pseudo-selector to an <img/>, but here I am setting ::after to the container and I don't get why it isn't working properly.

Why is this happening and how could I make ::after actually the last element of testDiv?

P.S. I can't set the container or any parent element with position: relative, since I need ::after to have the same width as the screen and not just to fit inside the container and I don't have any element with the same width as the screen.

P.S. The margin-left:100px is just to make it easier to be seen in the snippet, therefore it doesn't matter to my purpose.

.testDiv > div::after {
  position: absolute;
  height: 2px;
  content: "";
  background-color: red;
  left: 0;
  right: 0;
}

.testDiv > div {
  margin-left: 100px;
}
<div class="testDiv">
    <div>
      <h1>TEST</h1>
      <h2>TEST</h2>
      <img src="https://www.acmetek.com/wp-content/uploads/rapidssl-logo.png" />
    </div>
</div>
Berg_Durden
  • 1,531
  • 4
  • 24
  • 46

3 Answers3

7

As far as I can see, neither existing answer actually address the question of why the line is over, not under the image. The reason can be found in CSS 2.2 Section 10.6.4. Absolutely positioned, non-replaced elements where it says if:

top and bottom are auto and height is not auto, then set top to the static position, set auto values for margin-top and margin-bottom to 0, and solve for bottom

and

the static position for top is the distance from the top edge of the containing block to the top margin edge of a hypothetical box that would have been the first box of the element if its specified position value had been static and its specified float had been none and its specified clear had been none.

Which means that the top of the absolute positioned box will be the top of where the box would have been had not been position:absolute.

Since both the img and the ::after pseudo element without position:absolute are display:inline they would have lined up alongside one another. The pseudo element taken out of the flow and stretched horizontally to meet the left:0;right:0 requirements.

What this means is that you can move the red line to below the img just by making the ::after pseudo element display:block.

This is quite an interesting effect given that we normally think of position:absolute blockifying the box anyway, but this only happens after the static position calculation.

Another solution, perhaps a little more intuitive, is to make the img element display:block.

.testDiv > div::after {
  position: absolute;
  height: 2px;
  content: "";
  background-color: red;
  left: 0;
  right: 0;
  display:block;
}

.testDiv > div {
  margin-left: 100px;
}
<div class="testDiv">
    <div>
      <h1>TEST</h1>
      <h2>TEST</h2>
      <img src="https://www.acmetek.com/wp-content/uploads/rapidssl-logo.png" />
    </div>
</div>
Alohci
  • 78,296
  • 16
  • 112
  • 156
  • exactly .. that was my duplicate(https://stackoverflow.com/q/54774549/8620333) to the question but it got reopened .. let's hope the OP will get convinced with your answer – Temani Afif Nov 25 '20 at 02:01
  • Thank you very much, @Alohci. Your answer is very clear. – Berg_Durden Nov 25 '20 at 02:06
  • 1
    @Temani Afif , I apologize, now that I read this answer I can see that the post you pointed out makes sense, but only now. When we are learning we need simple explanations, as this one. The other post answers the question, but it's all very technical, if I had this level of understanding I could have figure it out by myself and I wouldn't have asked. Thank you very much for your time. – Berg_Durden Nov 25 '20 at 02:12
  • as a side note, since the vertical alignment is baseline, the pseudo element would have been located next to the image but at the bottom of the line so I would imagine that the static position will pick the top of the linebox – Temani Afif Nov 25 '20 at 02:22
  • 1
    @TemaniAfif you're right, but at the time the question looked different. Meanwhile I've closed as dupe – dippas Nov 25 '20 at 10:14
  • 1
    @TemaniAfif - The point that the static position seems to based on it having `vertical-align:top` bothers me a bit. While it's clear that it's the only vertical alignment that wouldn't be problematic by either affecting the vertical position of all the other elements on the line, or else results in an indeterminate static position depending on what happens to the line box layout when the box is removed from the flow, I don't know of anything in the CSS specs that calls for it. Have you come across any spec requirement that justifies it? – Alohci Nov 27 '20 at 01:42
  • 1
    not really, I accepted the fact that using the sentence *.. user agents are free to make a guess at its probable position.* in [the Spec](https://www.w3.org/TR/CSS22/visudet.html) means that we don't have precise rules to define the static position and browsers are free to take the *easiest* one – Temani Afif Nov 27 '20 at 02:49
2

You can still use position:relative but have your element overflow from both sides:

.testDiv>div::after {
  position: absolute;
  height: 2px;
  content: "";
  background-color: red;
  left: -100vw; /* big value here */
  right: 0;
  bottom: 0;
  box-shadow: 100vw 0 0 red; /* big value here too*/
}

.testDiv>div {
  margin-left: 100px; /* we have a margin */
  width: 200px; /* and a width too */
  position: relative;
}
<div class="testDiv">
  <div>
    <h1>TEST</h1>
    <h2>TEST</h2>
    <img src="https://www.acmetek.com/wp-content/uploads/rapidssl-logo.png" />
  </div>
</div>
<div class="testDiv">
  <div>
    <h1>Another one </h1>
    <h2>TEST</h2>
    <img src="https://www.acmetek.com/wp-content/uploads/rapidssl-logo.png" />
  </div>
</div>
dippas
  • 58,591
  • 15
  • 114
  • 126
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
1

Given your comment

It doesn't solve my problem, since I need the ::after to have the width of the screen and not only of the container

You need to set position:relative to .testDiv parent and also add bottom:0 and width:100% to the pseudo element

.testDiv {
  position: relative
}

.testDiv>div {
  margin-left: 100px;
}

.testDiv>div::after {
  position: absolute;
  height: 2px;
  content: "";
  background-color: red;
  left: 0;
  bottom: 0;
  width: 100%
}
<div class="testDiv">
  <div>
    <h1>TEST</h1>
    <h2>TEST</h2>
    <img src="https://www.acmetek.com/wp-content/uploads/rapidssl-logo.png" />
  </div>
</div>

UPDATE - based on your comment

you will need either set bottom: (any value)(any unit you need) or top: (any value)(any unit you need)

.testDiv>div::after {
  position: absolute;
  height: 2px;
  content: "";
  background-color: red;
  left: 0;
  bottom: 50px;
  width: 100%
}
<div class="testDiv">
  <div>
    <h1>TEST</h1>
    <h2>TEST</h2>
    <img src="https://www.acmetek.com/wp-content/uploads/rapidssl-logo.png" />
  </div>
</div>
dippas
  • 58,591
  • 15
  • 114
  • 126
  • It would work if `testDiv` had the same width as the screen, but that's not the case. This is just an example. I really need to set `::after` based on the screen and not on any parent element. I edited my post to make it a bit clearer. – Berg_Durden Nov 25 '20 at 00:22
  • @Berg_Durden see updated answer – dippas Nov 25 '20 at 00:27
  • It works. It's better than moving my margins. So, it seems that the only way is moving something. Just for educational purposes, do you know why `::after` is not positioned after ``? As I said in the question, I understand why I can't put a pseudo-selector to an img tag, but I believed that in this case it would work, since I am using a `div`. – Berg_Durden Nov 25 '20 at 00:36
  • 1
    it is all about the `position` attributes, you were missing `top`(or) `bottom`, and by default the values to them are `auto` making the line in the middle – dippas Nov 25 '20 at 00:38
  • I thought about that, but what is bugging me is that when I have only text inside the div it works properly and `::after` is positioned as the last element, but when I have specifically an image it doesn't happen. – Berg_Durden Nov 25 '20 at 00:42