Since there are no spaces in between the <span>
elements, I expect them not to wrap but all sit on the same line, which seems to be the case in all browsers
span {
position: relative;
}
<span>Fooooooooooooooooooooooooooooooooooooooooooooooooo</span><span>foo</span><span>Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar</span><span>bar</span>
But when adding a ::before
tooltip element this behaviour changes. While they stay on the same line in Firefox (114), they start wrapping in Safari (16) and Chrome (114).
span {
position: relative;
}
span::before {
content: 'tooltip';
position: absolute;
left: 0;
top: 0;
opacity: 0;
background: grey;
color: white;
transition: opacity 100ms;
}
span:hover::before {
opacity: 100;
}
<span>Fooooooooooooooooooooooooooooooooooooooooooooooooo</span><span>foo</span><span>Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar</span><span>bar</span>
Why is that and is there a way to prevent it?
Different display
options on the ::before
elements didn’t have an effect.
But I just noticed that setting inline-block
on the span
s actually seems to have the same effect as adding the ::before
element
span {
position: relative;
display: inline-block;
}
<span>Fooooooooooooooooooooooooooooooooooooooooooooooooo</span><span>foo</span><span>Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar</span><span>bar</span>
Is there a logical explanation for the wrapping, or can this be considered unexpected behaviour?
Since in the real example there are whitespaces which should break, wrapping everything in a white-space: nowrap;
container isn’t an option.
Here’s an example with whitespaces where FoooooooBaaaaaaar
should stay together and not split into two lines
span {
position: relative;
}
span::after {
content: 'tooltip';
position: absolute;
left: 0;
top: 0;
opacity: 0;
background: grey;
color: white;
transition: opacity 100ms;
}
span:hover::after {
opacity: 100;
}
<span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span>
While the abstract examples already illustrate the problem, here’s also a more realistic one: Given a sentence that contains special characters such as quotation marks and parentheses, and words that are a combination of two words. For styling purposes, and because the words have a translation tooltip, everything is split into span elements.
span {
position: relative;
}
.word::after {
content: 'tooltip';
position: absolute;
left: 0;
top: 0;
opacity: 0;
background: grey;
color: white;
transition: opacity 100ms;
}
.word:hover::after {
opacity: 100;
}
This is an "example sentence" containing a comboword and also something in (parenthesis).<br><br>
<span class="word">This</span><span class="char"> </span><span class="word">is</span><span class="char"> </span><span class="word">an</span><span class="char"> </span><span class="char">"</span><span class="word">example</span><span class="char"> </span><span class="word">sentence</span><span class="char">"</span><span class="char"> </span><span class="word">containing</span><span class="char"> </span><span class="word">a</span><span class="char"> </span><span class="word">combo</span><span class="word">word</span><span class="char"> </span><span class="word">and</span><span class="char"> </span><span class="word">also</span><span class="char"> </span><span class="word">something</span><span class="char"> </span><span class="word">in</span><span class="char"> </span><span class="char">(</span><span class="word">parenthesis</span><span class="char">)</span><span class="char">.</span>
Usually there wouldn’t be a break between the special characters and the word next to it or in betweeen the comboword, but with the ::after
element that’s the case.
3
Answers
This will prevent the spans from wrapping to the next line.
By setting
white-space: nowrap;
on the containing element, you can prevent wrapping even when using::after
elements or having whitespaces in the content.By default, inline elements have a shrink to fit behavior, which means they will try to fit on a single line and wrap if necessary. However, now when you add absolute positioning to the
:before
element, it takes the element out of the normal flow of the document, and the span elements revert to their default behavior of sitting on separate lines.One way to solve this is to add display flex to the parent container of said
span
elements as this will make them sit on the same line, but still break on whitespaces.I made the
:before
becomeposition: relative
on hover, so it does not clip into the span elements, this is not necessary if you want it to show over the other elements, just make sure you add az-index
to it then, so it gets placed on top of the span it is related to.I hope this is the answer you are looking for, if you need any more help just let me know!
I was reading the specs here and it says that:
According to my understanding,
position: absolute
should not cause the elements to wrap and Chromimum based browsers are doing it wrong!The article further states that:
So
display: inline-block
could a soft line break.