首页 > 解决方案 > How to make wrapped text's background span exactly across the pixel distance of that text?

问题描述

This is a problem I thought is simple enough for the modern CSS to solve and I'm really confused now. I wanted to make text parent's background to span exactly across the distance between left and right of it's longest text line, and spread evenly on the sides (not like the span element does)

It's obvious that the block elements automatically goes 100% width, and inline-block would be ok, if not the behavior of text's auto wrapping mechanism, which is making bounding box 100% size as it wraps first time to the line below.

I made a simulation of a desired effect, but this workarounds are somewhat ugly. I'm interested into more simple, native solution.

Fiddle + simulation: https://jsfiddle.net/sxmkonLp/20/

Normal block element text

<div class="container normal">
  <h1 class="fill">Gonna wrap my text around</h1>
</div>

Parent element display: "inline-block" (a little closer)

<div class="container">
  <h1 class="fill inl-blk">Gonna wrap my text around</h1>
</div>

Text inside "span" element (closer to what I need, but not evenly spread on the sides, depends on each separate line's length)

<div class="container">
  <h1>
    <span>Gonna wrap my text around</span>
  </h1>
</div>

Desired result (simulating text breaks with br and classes)

<div class="container text-span">
  <h1 class="fill2 inl-blk">Gonna<br class="sm4"> wrap<br class="sm3"> my<br class="sm2 sm4"> text<br class="sm1 sm3 sm4"> around</h1>
</div> 

There goes CSS for the markup

html, body {
  text-align: center;
  margin: 0;
  padding: 0;
}

.container {
  padding: 10px;
  width: auto;
  background: orange;
  margin: 10px;
}

.container span {
  background: lightgreen;
  padding: 5px;
}

.inl-blk {
  display: inline-block;
}

.fill {
  background: yellow;
}
.fill2 {
  background: blueviolet;
  color: white;
}

.sm1,.sm2,.sm3,.sm4 {display: none;}

@media screen and (max-width: 470px) {
  .sm2 {display: none;}
  .sm3 {display: none;}
  .sm4 {display: none;}
  .sm1 {display: block;}
}
@media screen and (max-width: 350px) {
  .sm1 {display: none;}
  .sm3 {display: none;}
  .sm4 {display: none;}
  .sm2 {display: block;}
}
@media screen and (max-width: 295px) {
  .sm1 {display: none;}
  .sm2 {display: none;}
  .sm4 {display: none;}
  .sm3 {display: block;}
}
@media screen and (max-width: 242px) {
  .sm1 {display: none;}
  .sm2 {display: none;}
  .sm3 {display: none;}
  .sm4 {display: block;}
}

As you can see in the last example (purple box) background occupies exactly the space of the text boundary. The problem is to make that happen while the text automatically wraps into the line below

I've made some discoveries recently, like this "exotic" CSS properties "box-decoration-break: clone;" which is making span's content more like separate, block lines, but still spans across separate line length, making it unusable for my current needs. It would be cool if we have some CSS property for exactly that.

标签: htmlcss

解决方案


我已经为这个问题开发了非常简单的 JS 解决方案。最后,几乎每个网站最终都有大量的 javascript,所以我觉得它可以忍受。我们需要的只是这个惊人的包“css-element-queries”中的“ResizeSensor”

所以,我们得到了一个简单的容器,它用 h1 和 div 保存在我们的标题中,并带有一个类“title-bg”,它将成为标题的“虚拟响应背景”。它从显示设置为内联的 h1 获取精确的文本宽度。

<div class="container">
  <div class="title">
    <h1>Should I learn Scratch before programming?</h1>
    <div class="title-bg"></div>
  </div>
</div>

这里是 CSS,注意“.title-bg”是绝对定位的,所以它需要 transform-50% 的解决方案来保持居中。还需要 z-index: 1 for h1 可见。

.container {
  max-width: 800px;
  min-height: 320px;
  padding: 20px;
  background: #ccc;
  margin: auto;
  text-align: center;
}

.title {
  overflow: hidden;
}

.title h1 {
  display: inline;
  /* background: orange; */
  position: relative;
  z-index: 1;
}

.title-bg {
  background: teal;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

而对于 JS,我们只需要设置 ResizeSensor 来监听事件。出于某种原因,我无法让它直接与 h1 元素一起使用(当设置为 display: inline 时),所以我将它附加到标题 div。剩下的就是从 h1.offsetWidth 中获取值,这正是文本长度的值。瞧,准备好工作了!

let title = document.querySelector('.title')
let h1 = document.querySelector('.title h1')
let titleBg = document.querySelector('.title-bg')

// fire once on start
titleBg.style.width = `${h1.offsetWidth}px`
titleBg.style.height = `${h1.offsetHeight}px`

// init so it auto fires on element resize
new ResizeSensor(title, ()=> {
    titleBg.style.width = `${h1.offsetWidth}px`
  titleBg.style.height = `${h1.offsetHeight}px`
})

自己尝试:http: //jsfiddle.net/ktdfqhzo/52/


推荐阅读