英文:
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 spans 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。



评论