如何仅使用 CSS 实现扰流引用?

Posted

技术标签:

【中文标题】如何仅使用 CSS 实现扰流引用?【英文标题】:How can I implement a spoiler quote with just CSS? 【发布时间】:2016-03-19 11:16:20 【问题描述】:

我有一个这样的blockquote

<blockquote class="spoiler">Soopah sekkrit!</blockquote>

我想让它隐藏起来,只有当用户将鼠标悬停在它上面时才会显示它。我现在用 JS 做:

blockquote.addEventListener('mouseover', function() 
    this.style.height = this.offsetHeight + 'px';
    this.dataset.contents = this.innerhtml;
    this.innerHTML = '';
);
blockquote.addEventListener('mouseout', function() 
    this.style.height = '';
    this.innerHTML = this.dataset.contents;
);

有没有更好的方法来做到这一点,使用 CSS?

它必须保持其background-color、大小,并适用于具有自定义颜色的内容。如果可能的话,我还想对其进行动画处理,以便内容逐渐淡入。

【问题讨论】:

【参考方案1】:

这与我在SOUP 中使用的非常相似:

.spoiler, .spoiler > *  transition: color 0.5s, opacity 0.5s 
.spoiler:not(:hover)  color: transparent 
.spoiler:not(:hover) > *  opacity: 0 
/* fix weird transitions on Chrome: */
blockquote, blockquote > *:not(a)  color: black 

.spoiler, .spoiler > *  transition: color 0.5s, opacity 0.5s 
.spoiler:not(:hover)  color: transparent 
.spoiler:not(:hover) > *  opacity: 0 
/* fix weird transitions on Chrome: */
blockquote, blockquote > *:not(a)  color: black 

/* some basic bg styles for demonstration purposes */
blockquote  background: #fed; margin: 1em 0; padding: 8px; border-left: 2px solid #cba 
code  background: #ccc; padding: 2px 
img  vertical-align: middle 
<blockquote class="spoiler">
  Soopah sekkrit text with <code>code</code> and <a href="#">links</a> and <img src="//sstatic.net/stackexchange/img/logos/so/so-logo-med.png"  /> images!
  <p>You can also have paragraphs in here.</p>
  <ul><li>And lists too!</li></ul>
  <blockquote class="spoiler">Even nested spoilers work!</blockquote>
</blockquote>

这比your own solution 稍微简单一些,适用于任意内容,包括图片甚至嵌套剧透! (参见上面的演示 sn-p。)

唉,如果剧透的任何子元素具有color: inherit,这种方法似乎会在 Chrome 上出现奇怪的过渡效果。 (基本上,正在发生的事情是这些元素将 both 将它们的文本颜色设置为透明,并将它们的不透明度设置为 0。因为不透明度以相乘方式组合,因此组合的过渡将显得更慢——在淡入淡出的中途——在,当元素本身的不透明度为 50% 时,其中的 text 为 50% × 50% = 25% 的不透明度。)我在上面的示例中添加了一个额外的 CSS 规则来修复这个,但它确实让事情变得有点复杂。


实际上在 SOUP 中所做的略有不同。我将每个扰流器的内容包装在一个额外的内部&lt;div&gt; 中,这让我可以将 CSS 进一步简化为:

.spoiler > div  opacity: 0; transition: opacity 0.5s 
.spoiler:hover > div  opacity: 1 

.spoiler > div  opacity: 0; transition: opacity 0.5s 
.spoiler:hover > div  opacity: 1 

/* some basic bg styles for demonstration purposes */
blockquote  background: #fed; margin: 1em 0; padding: 8px; border-left: 2px solid #cba 
code  background: #ccc; padding: 2px 
img  vertical-align: middle 
<blockquote class="spoiler"><div>
  Soopah sekkrit text with <code>code</code> and <a href="#">links</a> and <img src="//sstatic.net/stackexchange/img/logos/so/so-logo-med.png"  /> images!
  <p>You can also have paragraphs in here.</p>
  <ul><li>And lists too!</li></ul>
  <blockquote class="spoiler"><div>Even nested spoilers work!</div></blockquote>
<div></blockquote>

这种方法的主要优点是简单和健壮:我不必使用:not() 选择器,提高了与旧浏览器的兼容性,并且transition 样式不能与元素上可能定义的其他转换冲突里面剧透。这种方法也不会受到上述 Chrome 上的颜色过渡怪异的影响,因为它只使用不透明度过渡。

总的来说,这是我推荐的方法。当然,缺点是您需要在 HTML 中包含额外的 &lt;div&gt;s。


Ps。请考虑提供一些方法来使剧透永久可见,尤其是对于可能很难将光标“悬停”在元素上的触摸屏用户。一个简单的解决方案是使用 javascript click 事件处理程序来切换 spoiler 类,例如像这样(使用 jQuery):

$('.spoiler').on( 'click', function (e) 
  $(this).toggleClass('spoiler');
  e.stopPropagation();
 );

$('.spoiler').on( 'click', function (e) 
  $(this).toggleClass('spoiler');
  e.stopPropagation();
 );
.spoiler > div  opacity: 0; transition: opacity 0.5s 
.spoiler:hover > div  opacity: 1 

/* some basic bg styles for demonstration purposes */
blockquote  background: #fed; margin: 1em 0; padding: 8px; border-left: 2px solid #cba 
code  background: #ccc; padding: 2px 
img  vertical-align: middle 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<blockquote class="spoiler"><div>
  Soopah sekkrit text with <code>code</code> and <a href="#">links</a> and <img src="//sstatic.net/stackexchange/img/logos/so/so-logo-med.png"  /> images!
  <p>You can also have paragraphs in here.</p>
  <ul><li>And lists too!</li></ul>
  <blockquote class="spoiler"><div>Even <a href="//example.com">nested</a> spoilers work!</div></blockquote>
<div></blockquote>

或者,如果您更喜欢使用委托事件处理(这样您就不必每次通过 Ajax 加载包含剧透的新内容时都添加新的点击处理程序):

$(document).on( 'click', '.spoiler, .spoiler-off', function (e) 
  $(this).toggleClass('spoiler').toggleClass('spoiler-off');
  e.stopPropagation();
 );

$(document).on( 'click', '.spoiler, .spoiler-off', function (e) 
  $(this).toggleClass('spoiler').toggleClass('spoiler-off');
  e.stopPropagation();
 );
.spoiler > div  opacity: 0; transition: opacity 0.5s 
.spoiler:hover > div  opacity: 1 

/* some basic bg styles for demonstration purposes */
blockquote  background: #fed; margin: 1em 0; padding: 8px; border-left: 2px solid #cba 
code  background: #ccc; padding: 2px 
img  vertical-align: middle 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<blockquote class="spoiler"><div>
  Soopah sekkrit text with <code>code</code> and <a href="#">links</a> and <img src="//sstatic.net/stackexchange/img/logos/so/so-logo-med.png"  /> images!
  <p>You can also have paragraphs in here.</p>
  <ul><li>And lists too!</li></ul>
  <blockquote class="spoiler"><div>Even <a href="//example.com">nested</a> spoilers work!</div></blockquote>
<div></blockquote>

(这些应该适用于上面显示的任一 CSS 变体。)

【讨论】:

&lt;code&gt; 的第一个示例中,我首先看到背景淡入,然后是之后的文本,而不是同时出现。 @bjb568:啊,我明白了问题所在:如果任何嵌套元素具有color: inherit,颜色过渡与不透明度过渡会奇怪地结合在一起。出于某种原因,这不会发生在 Firefox 上,这就是为什么我最初没有注意到它,但它确实发生在 Chrome 上。我已经编辑了上面的示例以在&lt;code&gt;&lt;blockquote&gt; 元素上设置color: black,这应该可以解决它。 (实际上只有第一个示例需要进行编辑,但为了保持一致性,我对所有示例都进行了修改。)【参考方案2】:

是的,这可以通过 CSS 实现。本质上,您希望使所有内容不可见。在 CSS 中,这意味着透明。

首先在not pseudo-class里面使用hover pseudo-class:

.spoiler:not(:hover)

但我们还需要选择悬停扰流板的所有子元素,设置它们的颜色和背景:

.spoiler:not(:hover) *

我们将颜色和背景(仅针对子元素)设置为transparent,以使它们对用户不可见。一起来:

.spoiler:not(:hover), .spoiler:not(:hover) *  color: transparent 
.spoiler:not(:hover) *  background: transparent 

code  padding: 2px; background: #bbb 
a  color: #00f 
Hover: &lt;blockquote class="spoiler"&gt;Some stuff &lt;a&gt;and a colored link&lt;/a&gt; &lt;code&gt;and some code!&lt;/code&gt;&lt;/blockquote&gt;

我们还可以添加transition 使其更流畅:

.spoiler  transition: color 0.5s  /* we have to put this outside the :hover to make it work fading both in and out */
.spoiler:not(:hover), .spoiler:not(:hover) *  color: transparent 
.spoiler *  transition: color 0.5s, background 0.5s 
.spoiler:not(:hover) *  background: transparent 

code  padding: 2px; background: #bbb; color: #000  /* add color to prevent double transition */
a  color: #00f 
Hover: &lt;blockquote class="spoiler"&gt;Some stuff &lt;a&gt;and a colored link&lt;/a&gt; &lt;code&gt;and some code!&lt;/code&gt;&lt;/blockquote&gt;

为了让用户清楚地看到块引用是可悬停的,您可以添加一些带有::after pseudo-element 的文本,以便在块引用未悬停时显示:

.spoiler  transition: color 0.5s; position: relative  /* relative position for positioning the pseudo-element */
.spoiler:not(:hover), .spoiler:not(:hover) *  color: transparent 
.spoiler *  transition: color 0.5s, background 0.5s 
.spoiler:not(:hover) *  background: transparent 
.spoiler::after 
    content: 'hover to view spoiler';
    position: absolute;
    top: 0; left: 0;
    color: transparent;

.spoiler:not(:hover)::after 
    color: #666;
    transition: color 0.3s 0.3s; /* delayed transition to keep the text from overlapping */


code  padding: 2px; background: #bbb; color: #000 
a  color: #00f 
<blockquote class="spoiler">
    Some stuff <a>and a colored link</a> <code>and some code!</code>
    <blockquote class="spoiler">Nesting bonus!</blockquote>
</blockquote>

对于图像、svgs(内联 SVG 可以非常精细地控制)、画布和所有花哨的东西,而不是 color,您必须使用 opacity。我们可以通过添加以下内容使其与这些一起使用:

.spoiler img  transition: opacity 0.5s, background 0.5s 
.spoiler:not(:hover) img  opacity: 0 

【讨论】:

【参考方案3】:

这是一个效果很好、看起来不错并且过渡非常干净的策略

.spoiler 
    position: relative;
    display: inline-block;
    cursor: help;

.spoiler::before 
    content: 'psst\02026'; /* &hellip; */
    position: absolute;
    left: -2px;
    top: -2px;
    right: -2px;
    bottom: -2px;
    border-radius: 1px;
    font-size: .9rem;
    color: #e6578c;
    background: #ffe5e5;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    opacity: 1;
    transition: opacity 0.7s ease, transform 0.3s ease; /* hide faster than reveal */

.spoiler:hover::before 
    opacity: 0;
    transform: translateY(-50%)rotateX(80deg);
    transition: opacity 1.0s ease, transform 0.5s ease; /* slower reveal */

如果您使用opacity: 0 为父块设置样式而不使用悬停,则您无法添加任何样式来说明用户应将鼠标悬停在页面的哪个部分。

相反,如果我们添加一个覆盖子内容的 ::before 元素,那么我们可以在悬停时将其淡出,并且仍然提供去哪里的视觉指示。

堆栈片段中的演示

.spoiler 
    position: relative;
    display: inline-block;
    cursor: help;

.spoiler::before 
    content: 'psst\02026'; /* &hellip; */
    position: absolute;
    left: -2px;
    top: -2px;
    right: -2px;
    bottom: -2px;
    border-radius: 1px;
    font-size: .9rem;
    color: #e6578c;
    background: #ffe5e5;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 1;
    transition: opacity 0.7s ease, transform 0.3s ease; /* hide faster than reveal */

.spoiler:hover::before 
    opacity: 0;
    transform: translateY(-50%)rotateX(80deg);
    transition: opacity 1.0s ease, transform 0.5s ease; /* slower reveal */





/* demo styles */
blockquote 
  margin: 0
<p>
  Inline Spoiler <span class="spoiler" > Word </span>
</p>

<p class="spoiler">
  Paragraph Text Block of a Spoiler
</p>

<blockquote class="spoiler">
  Block quote spoiler with super long text that wraps and wraps and wraps some more.
  
  Block quote spoiler with super long text that wraps and wraps and wraps some more.
  
  Block quote spoiler with super long text that wraps and wraps and wraps some more.
</blockquote>

【讨论】:

以上是关于如何仅使用 CSS 实现扰流引用?的主要内容,如果未能解决你的问题,请参考以下文章

如何仅使用 HTML 和 CSS 创建爱尔兰国旗? [复制]

如何使用 CSS 仅显示字符串的前 10 个字符?

winform 中如何引用js和css??

如何仅使用 CSS 禁用链接

如何仅使用 CSS 创建箭头? [复制]

如何仅使用 css 获取元素的高度