【问题标题】:How to create a pure CSS tooltip with HTML content for inline elements如何为内联元素创建带有 HTML 内容的纯 CSS 工具提示
【发布时间】:2017-03-24 16:24:16
【问题描述】:

TL;DR - 请参阅 Adam 的回答(已接受)或我的 Update #5 当前解决方案的问题。我的问题显示了我的旅程 以相当长但解释性的方式获得该解决方案, 描述陷阱和限制。

我正在创建一个模块,用于将词汇表术语描述添加到文本中使用的各个单词中。纯文本描述一切正常。例如,如果有对 onomasicon(同义词库的另一个词)的描述,则以下 HTML

<p>An onomasticon is not a dinosaur.</p>

转换成

&lt;p&gt;An &lt;dfn title="Another word for thesaurus"&gt;onomasticon&lt;/dfn&gt; is not a dinosaur.&lt;/p&gt;

由于内容(文章文本和词汇表术语描述)来自允许用户使用 HTML 的 CMS,因此术语的描述可能包含 HTML,例如Another word for &lt;strong&gt;thesaurus&lt;/strong&gt;。使用上述技术,这开始变得混乱:

&lt;p&gt;An &lt;dfn title="Another word for &lt;strong&gt;thesaurus&lt;/strong&gt;"&gt;onomasticon&lt;/dfn&gt; is not a dinosaur.&lt;/p&gt;

这是默认设置,因为标签属性可能不包含标签本身。这个限制可以通过在dfn标签内添加一个额外的元素并添加一些CSS来隐藏元素来绕过:

dfn span {
  display: none;
}
dfn:hover span {
  display: block;
  position: absolute;
  top: 2.2em;
  background: #ccc;
}
&lt;p&gt;An &lt;dfn&gt;&lt;span&gt;Another word for &lt;strong&gt;thesaurus&lt;/strong&gt;&lt;/span&gt;onomasticon&lt;/dfn&gt; is not a dinosaur.&lt;/p&gt;

但现在到了,我没能克服。像&lt;strong&gt; 这样的HTML 标签是不会造成问题的内联元素。但是,如果术语的描述包含块元素,则会失败,例如如果描述本身包含在&lt;p&gt; 标签中:

dfn span {
  display: none;
}
dfn:hover span {
  display: block;
  position: absolute;
  top: 2.2em;
  background: #ccc;
}
&lt;p&gt;An &lt;dfn&gt;&lt;span&gt;&lt;p&gt;Another word for &lt;strong&gt;thesaurus&lt;/strong&gt;&lt;/p&gt;&lt;/span&gt;onomasticon&lt;/dfn&gt; is not a dinosaur.&lt;/p&gt;

这里发生了什么?好吧,由于大多数内联元素中不允许使用块元素,因此外部内联元素将被终止,而内部块元素则紧随其后。因此,CSS 选择器不再起作用,而且您最终会遇到额外的换行符。当然,有人可能会考虑添加类似dfn span p { display: inline-block; } 的内容,但这也不起作用(inline 也不行),它是无效的 HTML5。

虽然语义不正确,但我尝试将 &lt;dfn&gt; 转换为 &lt;div class="dfn"&gt; 并将内部 &lt;span&gt; 转换为 &lt;div&gt; 以及因为 dfn、abbr 和 cite 元素也可能只包含短语上下文(就像 span) .由于 &lt;div&gt; 不允许在 &lt;p&gt; 内,这再次失败,再次添加了不需要的换行符:

.dfn > div {
  display: none;
}
.dfn:hover div {
  display: inline-block;
  position: absolute;
  top: 2.2em;
  background: #ccc;
}
&lt;p&gt;An &lt;div class="dfn"&gt;&lt;div&gt;&lt;p&gt;Another word for &lt;strong&gt;thesaurus&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;onomasticon&lt;/div&gt; is not a dinosaur.&lt;/p&gt;

这是我迄今为止的旅程总结,试图达到将工具提示(包含 HTML)添加到内联元素(例如 &lt;dfn&gt;&lt;abbr&gt;&lt;cite&gt;)的圣杯 - 仅使用 CSS .

在我现在研究基于 javascript 的解决方案之前,我的问题是我是否错过了满足我下面列出的要求的选项?

  • 仅 CSS
  • 工具提示必须添加到段落内的&lt;dfn&gt;&lt;abbr&gt;&lt;cite&gt;
  • 工具提示内容可能包含以下 HTML 标签:div、p、h2、img、figure、ul、ol、li

更新 1: 因为我编写了模块 (PHP),所以我当然能够操纵术语的描述。但是,我不只是想剥离所有 &lt;p&gt; 标记,而是尝试保留预期的标记。

更新 2: 我找到了一个相当粗略的解决方案。假设onomasticon 的描述是一些 HTML sn-p 之类的:

<p>Another word for <strong>thesaurus</strong></p>
<p><img src="thesaurus.png" /></p>

我的模块现在会将所有块元素转换为具有适当类名的 span,因此描述变为

<span class="p">Another word for <strong>thesaurus</strong></span>
<span class="p"><img src="thesaurus.png" /></span>

这样,我就有了不应破坏短语上下文的内联元素。添加更多的 CSS 来设置工具提示的样式并让 span.p 再次表现得有点像 p,这一切都结束了。

dfn {
  display: inline-block;
  position: relative;
}
dfn > span {
  display: none;
}
dfn:hover > span {
  display: block;
  position: absolute;
  top: calc(1em + 5px);
  max-width: 20em;
  background: #ccc;
  padding: .5em;
}
dfn img {
  max-width: 12em;
}
dfn > span span.p {
  display: block;
  margin-bottom: .5em;
}
<p>An <dfn><span><span class="p">Another word for <strong>thesaurus</strong></span>
    <span class="p"><img src="http://i.imgur.com/G0bl4k7.png" /></span></span></span>onomasticon</dfn> is not a dinosaur.</p>

这是迄今为止我最好看的选项,它支持有限的 HTML 用于内联元素的工具提示。但是,它可能不是最好的选择,因为它需要对 HTML 块元素进行 hacky 重写。

更新 3: ChristianF 的回答有一个非常重要的线索,即如何在词汇表术语定义中保留 HTML。如果描述不是 dfn 标签 inside 的元素,而是后面的兄弟,我们仍然可以隐藏和显示它,例如使用 CSS 选择器 dfn + div.dfn-tooltip。但是,由于大多数词汇表术语会在短语上下文中找到,这将再次破坏布局,因为段落中不允许使用 div。

所以我们需要一个元素,既可以在短语上下文中使用,又可以包含块元素。根据这个beautiful HTML structure diagram&lt;button&gt; 标签将是唯一合适的标签。

p {
  display: relative;
}
dfn + .dfn-tooltip {
  display: none;
}
dfn:hover + .dfn-tooltip {
  display: block;
  position: absolute;
  top: 3em;
  max-width: 20em;
  background: #ccc;
  padding: .5em;
  border: none;
}
.dfn-tooltip img {
  max-width: 12em;
}
&lt;p&gt;An &lt;dfn&gt;onomasticon&lt;/dfn&gt;&lt;button class="dfn-tooltip"&gt;&lt;p&gt;Another word for &lt;strong&gt;thesaurus&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.imgur.com/G0bl4k7.png" /&gt;&lt;/p&gt;&lt;/button&gt; is not a dinosaur.&lt;/p&gt;

这看起来很有希望。当然,有两个主要缺点:(1)这个地方的按钮元素在语义上是不正确的;(2)工具提示在悬停时不会保持打开状态,因为它不再是 dfn 标签的子元素。

更新 4: 进一步避免第二个缺点是只需将按钮元素移回 dfn 标记,因为上下文是相同的。

dfn {
  position: relative;
  display: inline-block;
}
dfn > .dfn-tooltip {
  display: none;
}
dfn:hover > .dfn-tooltip {
  display: block;
  position: absolute; 
  top: calc(1em + 5px);
  max-width: 20em;
  background: #ccc;
  padding: .5em;
  border: none;
}
.dfn-tooltip img {
  max-width: 12em;
}
&lt;p&gt;An &lt;dfn&gt;onomasticon&lt;button class="dfn-tooltip"&gt;&lt;p&gt;Another word for &lt;strong&gt;thesaurus&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.imgur.com/G0bl4k7.png" /&gt;&lt;/p&gt;&lt;/button&gt;&lt;/dfn&gt; is not a dinosaur.&lt;/p&gt;

Update 5(final):Adam 的回答提出了一些很好的技巧来结合 title 属性的初衷,把所有东西放在一起,这就是我最终的结果。

dfn {
  position: relative;
  display: inline-block;
  cursor: help;
}
dfn:before {
  content: attr(title);
}
dfn > .dfn-tooltip {
  display: none;
}
dfn:hover > .dfn-tooltip {
  display: block;
  position: absolute; 
  top: calc(1em + 5px);
  max-width: 20em;
  background: #ccc;
  padding: .5em;
  border: none;
  cursor: help;
}
.dfn-tooltip img {
  max-width: 12em;
}
&lt;p&gt;An &lt;dfn title="onomasticon"&gt;&lt;button disabled class="dfn-tooltip"&gt;&lt;p&gt;Another word for &lt;strong&gt;thesaurus&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.imgur.com/G0bl4k7.png" /&gt;&lt;/p&gt;&lt;/button&gt;&lt;/dfn&gt; is not a dinosaur.&lt;/p&gt;

顺便说一句,如果有人感兴趣,我在一个名为 Onomasticon 的模块中使用它,它为 Drupal 8 提供了基本的词汇表功能。

【问题讨论】:

  • 如果您的 /requirements/ 要求您能够在段落中添加段落,那么您就处于障碍状态。这些要求是从哪里来的?您愿意改变这些要求吗?
  • @andi 这是一个基于 Drupal 的网站的模块。目前,客户可以在术语描述中仅使用纯文本,但在发布后,他们寻求一种解决方案,在该解决方案中,他们也能够在描述中添加段落和可能的图像。这本身没有问题,因为 Drupal 提供了一个富文本编辑器来做到这一点。我正在努力将富文本内容放入工具提示中。由于 Drupal 本身依赖 jQuery,我可以使用 jQuery,但我想避免在我的模块中添加另一个 JS 库(依赖项)。
  • 是否有任何硬性要求阻止您将 p 标签替换为一个(或两个)br 标签?诚然,这在语义上不是最干净的解决方案,但仍然完全合法。
  • 基本上,这就是我现在所做的,虽然有点不同(参见更新 2)。我将 &lt;p&gt; 替换为 &lt;span class="p"&gt; 并添加以下 CSS:span.p {display: block; margin-bottom: .5em;}

标签: php html css tooltip


【解决方案1】:

请注意,W3C 说:

定义术语:如果 dfn 元素具有 title 属性,则该属性的确切值就是正在定义的术语

因此,您可以有一个简单的解决方案,将正在定义的术语放在 title 属性中,并将定义放在里面

dfn::before {content: attr(title);padding: 0 0 1em;}      
dfn button {display: none; position: absolute}
dfn:hover button, dfn:focus button {display: block;}
<p>An
     <dfn title="onomasticon" tabindex="0"><button disabled>
       <p>Another word for <strong>thesaurus</strong></p>
       <p><img src="http://i.imgur.com/G0bl4k7.png" /></p>
      </button></dfn> is not a dinosaur.
</p>

我没有找到任何可以在这里替换 button 元素的标签,这似乎是唯一一个在这里工作的标签。

所以我们必须将disabled 属性添加到button 元素以禁用其button 行为(焦点)并在dfn 元素上设置tabindex 以启用箭头导航。

【讨论】:

  • 很好地抓住了 dfn 的 title 属性。我之前读过它并且总是感到困惑,因为即使在 w3schools 的示例中,title 属性也包含“超文本标记语言”,而 dfn 的内部文本是“HTML”(我认为这是正在定义的术语)。我喜欢你为 ::before 伪元素使用 title 属性的方式。
【解决方案2】:

正如已在 cmets 中建立的,最简单的方法是简单地将 p 标记替换为其他内容。这需要最少的解决方法,因此该方法最不可能中断。

最快的方法是将开头的p 标记替换为两个br 标记,然后将结尾替换为一个空字符串。这样,您将在两个文本块之间获得一个可见的空行,类似于通常给您的分节符。大多数情况下,这足以让人产生断句的错觉。

如您所见,另一种解决方案是用 span-tag 替换它们,并将它们的样式设置为与普通 P-tag 完全相同。这需要更多的代码和工作,但有利于表现,嗯...就像您网站上的段落一样,营造出一种完美的错觉,即那里实际上有一个段落中断。
如果段落的样式以不寻常的方式设置,这是我会使用的方法,因此简单的空行不足以产生错觉。例如,当第一行缩进时,第一个字符的特殊样式等等。

在您的情况下,我认为一个简单的双精度 br 就足够了。看到您在段落中只有一个.5em 底部边距。

编辑,添加: 根据您对我的回答的评论,我还有一个想法。也就是创建一个隐藏的div,并将评论存放在这个里面。给它一个对应于dfn 标记的rel 属性的ID。然后,使用 CSS 和 JS,将 DIV 设置为悬停时的工具提示。将需要更多的工作,但它可以让您保留所有格式和标签而不会出现问题。
对于那些不支持必要功能的人,您可以在标题标签内使用精简版。然后在显示“增强”工具提示时使用相同的JS来抑制它。

但是,您肯定会在这里使用 JS。除非您可以使用 CSS3 adjacent sibling selector

【讨论】:

  • 感谢您的回答。请注意,我的示例故意保持非常简单。在生产站点上,用户可以添加更多的 HTML,而不仅仅是段落和图像,例如有列表&lt;ul&gt;/&lt;ol&gt; 或标题&lt;hx&gt;。虽然该模块可以添加这些标签的替换以及在 cmets 和您的答案中提到的,但我实际上仍在寻找一种解决方案,它不需要篡改用户最初添加的 HTML。
  • 关于相邻兄弟选择器的编辑的最后一句话是最重要的部分。我已经用基于该线索的示例更新了我的问题。好主意,我喜欢你的想法。 ;)
  • 在上面提到的更新 3 之后,我刚刚做了另一个更新 #4,将描述移回 dfn 标签。尽管使用按钮元素存在语义错误,但这似乎是目前最好的选择。由于您的帖子将我带到那里,如果没有人提出更好的解决方案,我将在本周末接受您的回答。同时我会做一些测试,非常感谢你让我走上正轨。
  • @Paul 我认为在这一点上我更喜欢 JS,但感谢您找到解决方案!即使它有点hacky。 :P 不客气,很高兴我能帮上忙。
  • 你,先生,让我走上正轨,赢得了赏金。但是,为了未来的访问者,我将 Adam 的答案标记为已接受,因为它最接近我最终在生产性网站上使用的内容。
【解决方案3】:

我使用了对象标签。这对我来说更有意义,因为您在文本中包含了信息。

dfn {
  position: relative;
  display: inline-block;
}
dfn > .dfn-tooltip {
  display: none;
}
dfn:hover > .dfn-tooltip,
.dfn-tooltip:hover {
  display: block;
  position: absolute;
  top: calc(1em + 4px);
  max-width: auto;
  background: #ccc;
  padding: .5em;
  border: none;
}
.dfn-tooltip span {
  max-width: 12em;
  float: left;
}
.dfn-tooltip img {
  max-width: 12em;
  float: left;
  clear: both;
}
<p>An <dfn>onomasticon
  <object class="dfn-tooltip">
    <p>Another word for <strong>thesaurus</strong></p>
    <p><img src="http://i.imgur.com/G0bl4k7.png" /></p>
  </object>
  </dfn> is not a dinosaur.
</p>

【讨论】:

  • 正如我在问题中所说,特定词汇表术语的描述是 CMS 中的一个实体,用户可以通过多种不同方式对其进行样式设置(CKEditor 可用)。通过我的模块,我想提取描述并将其添加到其他内容中的相应术语中。虽然我知道短语上下文元素中的段落(也不是按钮)不是 HTML5 有效的,但我无法控制这些术语的描述。
  • 我调整了答案
  • 在寻找合适的标签时,我当然也偶然发现了&lt;object&gt;。然而,即使这和&lt;button&gt; 在语义上是不正确的,&lt;button&gt; 的优点是它确实在没有任何属性的情况下进行验证。对象标记需要设置数据或类型(数据是 URL 并键入有效的 IANA 媒体类型)。
【解决方案4】:

另一个值得研究的解决方案是使用纯 CSS,仅结合元素。忘记标题,或者简单地添加它。

如果您有能力生成和使用内联 css,并在输出被提供之前将其注入头部,那么如何:

The &lt;abbr id="abbr_1" title="Fox is an animal"&gt;fox&lt;/abbr&gt;. Another &lt;abbr id="abbr_2"&gt;word&lt;/abbr&gt;

abbr
{
   display: none;
   position: absolute;
   z-index: 1; //probably not required, but I hope the title won't show up above it.... I guess it won't.
}
abbr:hover{display: block;}
#abbr_1:before{
   content: '<p>Fox is a <i class="">beautiful</i> \'animal\'</p>'
}
...etc

别忘了转义单引号 ;)

【讨论】:

  • 标签不会在 css 元素的 content 属性内解释。
猜你喜欢
  • 2013-12-11
  • 1970-01-01
  • 1970-01-01
  • 2013-06-27
  • 2015-09-13
  • 1970-01-01
  • 2017-10-25
  • 2011-08-03
相关资源
最近更新 更多