英文:
How to prevent ::before / ::after elements influencing the breaking behaviour of spans?
问题
由于<span>
元素之间没有空格,我希望它们不会换行,而是都在同一行上,这似乎在所有浏览器中都是如此。
但是,当添加一个::before
工具提示元素时,这种行为会发生变化。虽然它们在Firefox(114)中保持在同一行上,但在Safari(16)和Chrome(114)中开始换行。
为什么会这样,有没有办法防止它发生?
在::before
元素上尝试不同的display
选项没有效果。
但我刚刚注意到,在span
上设置inline-block
实际上似乎具有与添加::before
元素相同的效果。
是否有换行的合理解释,或者可以认为这是意外的行为?
由于在实际示例中有应该换行的空格,将所有内容包装在white-space: nowrap;
容器中不是一个选项。
以下是一个示例,其中包含空格,其中FoooooooBaaaaaaar
应该保持在一起,而不应拆分成两行。
虽然抽象示例已经说明了问题,但这里还有一个更现实的示例:给定一个包含引号和括号等特殊字符的句子,以及由两个单词组合而成的词。出于样式和翻译提示的目的,将所有内容分割为span元素。
通常,在特殊字符和旁边或comboword之间不会有换行,但使用::after
元素会出现这种情况。
英文:
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
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
span {
position: relative;
}
<!-- language: lang-html -->
<span>Fooooooooooooooooooooooooooooooooooooooooooooooooo</span><span>foo</span><span>Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar</span><span>bar</span>
<!-- end snippet -->
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).
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
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;
}
<!-- language: lang-html -->
<span>Fooooooooooooooooooooooooooooooooooooooooooooooooo</span><span>foo</span><span>Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar</span><span>bar</span>
<!-- end snippet -->
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
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
span {
position: relative;
display: inline-block;
}
<!-- language: lang-html -->
<span>Fooooooooooooooooooooooooooooooooooooooooooooooooo</span><span>foo</span><span>Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar</span><span>bar</span>
<!-- end snippet -->
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
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
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;
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
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.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
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;
}
<!-- language: lang-html -->
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>
<!-- end snippet -->
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.
答案1
得分: 6
我在这里阅读了规范,上面写着:
流外元素和内联元素边界不会引入强制换行或软换行的机会。
显然,position: absolute
元素以及</span><span>
不应生成软换行;基于Chromium的浏览器做错了!
文章进一步指出:
为了Web兼容性,在每个替换元素或其他原子内联元素之前和之后都有软换行的机会,即使相邻的字符通常会抑制它们,比如U+00A0 NO-BREAK SPACE。
所以,display: inline-block
元素可能会生成软换行。
英文:
I was reading the specs here and it says that:
> Out-of-flow elements and inline element boundaries do not introduce a
> forced line break or soft wrap opportunity in the flow.
Apparently, position: absolute
elements as well as </span><span>
should not generate a soft wrap; Chromimum based browsers are doing it wrong!
The article further states that:
> For Web-compatibility there is a soft wrap opportunity before and
> after each replaced element or other atomic inline, even when adjacent
> to a character that would normally suppress them, such as U+00A0
> NO-BREAK SPACE.
So display: inline-block
elements could generate a soft wrap.
<!-- begin snippet: js hide: true console: false babel: false -->
<!-- language: lang-css -->
body {
font: medium monospace;
}
.test > span:nth-child(odd) {
background-color: #FC0;
}
.test > span:nth-child(even) {
background-color: #CF0;
}
.test-1 > span {
position: relative;
}
.test-1 > span::before {
content: "tooltip";
position: absolute;
left: 0;
top: 0;
color: #FFF;
background-color: rgb(0, 0, 0, .65);
}
.test-2 > span {
display: inline-block;
}
<!-- language: lang-html -->
<h1>position: absolute<br>should not wrap</h1>
<p class="test test-1"><span>Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</span><span>Bbbbbbbbbbbbbbbbbbbb</span><span>Cccccccccccccccccccccccccccccc</span><span>Dddddddddddddddddddddddddddddddddddddddddddddddddd</span></p>
<h1>display: inline-block<br>should wrap</h1>
<p class="test test-2"><span>Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</span><span>Bbbbbbbbbbbbbbbbbbbb</span><span>Cccccccccccccccccccccccccccccc</span><span>Dddddddddddddddddddddddddddddddddddddddddddddddddd</span></p>
<!-- end snippet -->
答案2
得分: 0
被设置为position:absolute
的元素被视为块级元素。
详细描述在这里 mdn/position,并在那里进一步链接 mdn/block formatting context (BFC)。
问题不在伪元素上。Chrome检查器确认:before
下的position:absolute
的display:block
在"Computed"下,而Firefox似乎没有分配"display"属性。
不幸的是,我没有一个100%的CSS解决方案。一种方法是使用display:none/block
来使所有浏览器中的换行相同。使用display:block
并且将一些样式传递给hover时,不幸的是会失去不透明度过渡效果,并且如果在行的开头有提示工具提示,行为会变得奇怪。
<!-- 开始片段:js 隐藏:false 控制台:true Babel:false -->
<!-- 语言:lang-css -->
span {
position: relative;
}
span::after {
display: none;
}
span:hover::after {
display: block;
content: 'tooltip';
position: absolute;
left: 0;
top: 0;
background: grey;
color: white;
transition: opacity 100ms;
}
<!-- 语言:lang-html -->
<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>
<!-- 结束片段 -->
你可能无法绕过JavaScript解决方案,其中您可以确定位置并相对于span的父元素设置(更全局的)工具提示。
英文:
Elements that are set to position:absolute
are treated as block elements.
The description is here mdn/position and further linked there mdn/block formatting context (BFC)
The problem is not the pseudo-element. The chrome inspector confirms display:block
for position:absolute
at :before
under "Computed", whereas Firefox does not seem to assign a "display" attribute.
Unfortunately I don't have a 100% CSS solution. One approach would be to work with display:none/block
so that the line breaks are the same in all browsers. With display:block
and and to transfering some sytles to hover you unfortunately lose the opacity transition effect and the line behaves strange if there is a tooltip at the beginning of the line.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
span {
position: relative;
}
span::after {
display: none;
}
span:hover::after {
display: block;
content: 'tooltip';
position: absolute;
left: 0;
top: 0;
background: grey;
color: white;
transition: opacity 100ms;
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
You probably won't get around a javascript solution, where you then determine positions and set a (more global) tooltip relative to the spans parent element.
答案3
得分: -2
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.
英文:
<div style="white-space: nowrap;">
<span>Fooooooo</span><span>Baaaaaaar</span> <span>Fooooooo</span><span>Baaaaaaar</span> ...
</div>
This will prevent the spans from wrapping to the next line.
<div style="white-space: nowrap;">
<span class="word">This</span><span class="char"> </span><span class="word">is</span> ...
</div>
By setting white-space: nowrap;
on the containing element, you can prevent wrapping even when using ::after
elements or having whitespaces in the content.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论