如何防止 ::before / ::after 元素影响 span 元素的换行行为?

huangapple go评论85阅读模式
英文:

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 -->

&lt;span&gt;Fooooooooooooooooooooooooooooooooooooooooooooooooo&lt;/span&gt;&lt;span&gt;foo&lt;/span&gt;&lt;span&gt;Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar&lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;

<!-- 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: &#39;tooltip&#39;;
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  background: grey;
  color: white;
  transition: opacity 100ms;
}

span:hover::before {
  opacity: 100;
}

<!-- language: lang-html -->

&lt;span&gt;Fooooooooooooooooooooooooooooooooooooooooooooooooo&lt;/span&gt;&lt;span&gt;foo&lt;/span&gt;&lt;span&gt;Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar&lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;

<!-- 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 -->

&lt;span&gt;Fooooooooooooooooooooooooooooooooooooooooooooooooo&lt;/span&gt;&lt;span&gt;foo&lt;/span&gt;&lt;span&gt;Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar&lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;

<!-- 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: &#39;tooltip&#39;;
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  background: grey;
  color: white;
  transition: opacity 100ms;
}

span:hover::after {
  opacity: 100;
}

<!-- language: lang-html -->

&lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt;

<!-- 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: &#39;tooltip&#39;;
  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 &quot;example sentence&quot; containing a comboword and also something in (parenthesis).&lt;br&gt;&lt;br&gt;

&lt;span class=&quot;word&quot;&gt;This&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;char&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;word&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;sentence&lt;/span&gt;&lt;span class=&quot;char&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;containing&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;combo&lt;/span&gt;&lt;span class=&quot;word&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;also&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;char&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;word&quot;&gt;parenthesis&lt;/span&gt;&lt;span class=&quot;char&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;char&quot;&gt;.&lt;/span&gt;

<!-- 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.

如何防止 ::before / ::after 元素影响 span 元素的换行行为?

答案1

得分: 6

我在这里阅读了规范,上面写着:

流外元素和内联元素边界不会引入强制换行或软换行的机会。

显然,position: absolute元素以及&lt;/span&gt;&lt;span&gt;不应生成软换行;基于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 &lt;/span&gt;&lt;span&gt; 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 &gt; span:nth-child(odd) {
  background-color: #FC0;
}
.test &gt; span:nth-child(even) {
  background-color: #CF0;
}
.test-1 &gt; span {
  position: relative;
}
.test-1 &gt; span::before {
  content: &quot;tooltip&quot;;
  position: absolute;
  left: 0;
  top: 0;
  color: #FFF;
  background-color: rgb(0, 0, 0, .65);
}
.test-2 &gt; span {
  display: inline-block;
}

<!-- language: lang-html -->

&lt;h1&gt;position: absolute&lt;br&gt;should not wrap&lt;/h1&gt;
&lt;p class=&quot;test test-1&quot;&gt;&lt;span&gt;Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&lt;/span&gt;&lt;span&gt;Bbbbbbbbbbbbbbbbbbbb&lt;/span&gt;&lt;span&gt;Cccccccccccccccccccccccccccccc&lt;/span&gt;&lt;span&gt;Dddddddddddddddddddddddddddddddddddddddddddddddddd&lt;/span&gt;&lt;/p&gt;

&lt;h1&gt;display: inline-block&lt;br&gt;should wrap&lt;/h1&gt;
&lt;p class=&quot;test test-2&quot;&gt;&lt;span&gt;Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&lt;/span&gt;&lt;span&gt;Bbbbbbbbbbbbbbbbbbbb&lt;/span&gt;&lt;span&gt;Cccccccccccccccccccccccccccccc&lt;/span&gt;&lt;span&gt;Dddddddddddddddddddddddddddddddddddddddddddddddddd&lt;/span&gt;&lt;/p&gt;

<!-- end snippet -->

答案2

得分: 0

被设置为position:absolute的元素被视为块级元素。

详细描述在这里 mdn/position,并在那里进一步链接 mdn/block formatting context (BFC)

问题不在伪元素上。Chrome检查器确认:before下的position:absolutedisplay: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: &#39;tooltip&#39;;
  position: absolute;
  left: 0;
  top: 0;
  background: grey;
  color: white;
  transition: opacity 100ms;
}

<!-- language: lang-html -->

&lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt;

<!-- 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

FoooooooBaaaaaaar FoooooooBaaaaaaar ...

This will prevent the spans from wrapping to the next line.

This is ...

By setting white-space: nowrap; on the containing element, you can prevent wrapping even when using ::after elements or having whitespaces in the content.

英文:
&lt;div style=&quot;white-space: nowrap;&quot;&gt;
  &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; &lt;span&gt;Fooooooo&lt;/span&gt;&lt;span&gt;Baaaaaaar&lt;/span&gt; ...
&lt;/div&gt;

This will prevent the spans from wrapping to the next line.

&lt;div style=&quot;white-space: nowrap;&quot;&gt;
  &lt;span class=&quot;word&quot;&gt;This&lt;/span&gt;&lt;span class=&quot;char&quot;&gt; &lt;/span&gt;&lt;span class=&quot;word&quot;&gt;is&lt;/span&gt; ...
&lt;/div&gt;

By setting white-space: nowrap; on the containing element, you can prevent wrapping even when using ::after elements or having whitespaces in the content.

huangapple
  • 本文由 发表于 2023年6月13日 01:42:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76459097.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定