webkit-line-clamp: text-align: right; breaks text-overflow: ellipsis; webkit-line-clamp: 文本溢出省略号; 文字对齐: 右对齐;

13

我的目标是:

  1. 限制文本行数
  2. 文本应向右对齐
  3. 文本超出部分用3个点表示

当我在样式中添加 text-align: right; 后,问题就出现了:与没有 text-align 样式属性时不同,3个点的行为也不同:通常,三个点在文本容器外(灰色)。

enter image description here

比较“好”vs“坏”的样式;唯一的区别是:“好”不包含 text-align: right; 样式属性。尝试在这些样式中调整字体大小,您会发现对于“好”样式,三个点始终在文本容器内(灰色背景),而对于“坏”样式,三个点的位置是意想不到的(可能在容器内、部分在内或完全在外面)。

那么,是否有可能实现三个点的“好”样式行为,同时使文本向右对齐?考虑行数限制。

* {
  box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #78b9f3;
  margin:0;
  font-family: "Arial";
}

.wrapper {
  display: flex;
  justify-content: center;
}

.card {
  background: #fff;
  box-shadow: 0 4px 24px 3px rgb(0 0 0 / 10%);
  padding: 20px;
  margin: 20px;
  width: 200px;
  height: auto;
  border-radius: 6px;
}

p {
  font-size: 1em;
  line-height: 1.3em;
  margin:0;
}

h2 {
  font-size: 1.5em;
  margin: 0 0 0.5em 0;
}

.good {
  font-size: 20px;
  background-color: grey;
   text-overflow: ellipsis;
   display: -webkit-box;
   -webkit-line-clamp: 2;
           line-clamp: 2; 
   -webkit-box-orient: vertical;
}

.bad {
  font-size: 20px;
  text-align: right;
  background-color: grey;
   text-overflow: ellipsis;
   display: -webkit-box;
   -webkit-line-clamp: 2;
           line-clamp: 2; 
   -webkit-box-orient: vertical;
}
<section class="wrapper">
  <div class="card">
    <h2>Good</h2>
    <p class="good">Hello, I'm a very long text for at least three lines!</p>
  </div>
  <div class="card">
    <h2>Bad</h2>
    <p class="bad">Hello, I'm a very long text for at least three lines!</p>
  </div>


</section>

7个回答

2

TL;DR: 没有100%可预测的CSS解决方案

只需查看MDN上的兼容性表格,除了Firefox外,浏览器支持相当不稳定(委婉地说)。

但是,您可以使用一些JavaScript解决此渲染问题。
...或者您也可以使用一些旧式的溢出和行高属性来模仿text-overflowline-clamp属性。

示例1:仅使用CSS的hack

此hack基于定义一些与line-height相关的类。
CSS变量有助于简化计算:

:root {
  --overflowLineHeight: 1.25em;
  --overflowColor1: rgba(255, 255, 255, 1);
  --overflowColor2: rgba(255, 255, 255, 0);
}


.maxLines2 {
  max-height: calc(var(--overflowLineHeight) * 2);
}

实际的溢出指示符(省略号)元素只是一个具有背景渐变的绝对定位伪元素(放置在相对定位的父元素中)。

* {
  box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #78b9f3;
  margin: 0;
  font-family: "Arial";
}

.wrapper {
  display: flex;
  justify-content: center;
  padding:1em;
}

.card {
  background: #fff;
  box-shadow: 0 4px 24px 3px rgb(0 0 0 / 10%);
  padding: 1em;
  margin: 1em;
  width: 33%;
  height: auto;
  border-radius: 6px;
  resize: both;
  overflow: hidden;
}

p {
  font-size: 1em;
  line-height: 1.3em;
  margin: 0;
}

h2 {
  font-size: 1.5em;
  margin: 0 0 0.5em 0;
}

:root {
  --overflowLineHeight: 1.25em;
  --overflowColor1: rgba(255, 255, 255, 1);
  --overflowColor2: rgba(255, 255, 255, 0);
}

.overflow {
  font-size: 20px;
  line-height: var(--overflowLineHeight);
  text-align: right;
  overflow: hidden;
  position: relative;
}

.overflow:after {
  content: " …";
  position: absolute;
  bottom: 0;
  right: 0;
  display: block;
  z-index: 1;
  background-image: linear-gradient( 90deg, var(--overflowColor2), var(--overflowColor1) 50%);
  width: 2.5em;
}

.maxLines2 {
  max-height: calc(var(--overflowLineHeight) * 2);
}

.maxLines3 {
  max-height: calc(var(--overflowLineHeight) * 3);
}

.maxLines4 {
  max-height: calc(var(--overflowLineHeight) * 4);
}
<section class="wrapper">
  <div class="card">
    <h2>Fix: 2 lines</h2>
    <p class=" maxLines2 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>

  <div class="card">
    <h2>Fix: 3 lines</h2>
    <p class=" maxLines3 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>

  <div class="card">
    <h2>Fix: 4 lines</h2>
    <p class=" maxLines4 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>

</section>

示例 2:针对line-clamp的js hack

这个hack基于一个事实,即word-break: break-all会在块边界内呈现省略号。

不幸的是,这将导致丑陋的断词。

为了解决这个问题,

  • 我们将所有单词拆分成一个<span>元素数组。
  • 然后我们检查所有的高度:如果一个的高度大于第一个实例 - 它包含一个换行符
  • 带有换行符的获得一个特殊类,具有word-break: break-word属性

let overflows = document.querySelectorAll(".overflow");
fixOverflow(overflows);

function fixOverflow(overflows) {
  overflows.forEach(function(text, i) {
    //split textContent into array
    let words = text.innerHTML
      .split(" ")
      .map((w) => {
        return w.trim();
      })
      .filter(Boolean);

    //delete current textContent
    let wrapped = "";
    text.textContent = "";
    text.style.setProperty("word-break", "break-all");
    let bbText = text.getBoundingClientRect();
    let bottomText = bbText.y + bbText.height;

    //get line height by checking the first word's height
    let span0 = document.createElement("span");
    span0.textContent = words[0] + " ";
    span0.classList.add("wordWrp");
    text.appendChild(span0);
    let height0 = span0.getBoundingClientRect().height;

    //check max lines and init counter
    let style = window.getComputedStyle(text);
    let maxLines = parseFloat(style.webkitLineClamp);
    let breaks = 0;

    //wrap words in spans
    for (let i = 1; i < words.length; i++) {
      let word = words[i];
      let span = document.createElement("span");
      span.textContent = word + " ";
      span.classList.add("wordWrp");
      text.appendChild(span);
      let bbSpan = span.getBoundingClientRect();
      let heightSpan = bbSpan.height;
      let bottomSpan = bbSpan.y + bbSpan.height;
      if (heightSpan > height0 && breaks < maxLines - 1 && bottomSpan < bottomText) {
        span.classList.add("wordWrpLine");
        breaks++;
      }
    }
  });
}


//update on resize 
const resizeObserver = new ResizeObserver(() => {
  upDateOverflows();
});
overflows.forEach(function(text, i) {
  resizeObserver.observe(text);
});

function upDateOverflows() {
  overflows.forEach(function(text, i) {
    let wordWraps = text.querySelectorAll(".wordWrp");
    let bbText = text.getBoundingClientRect();
    let bottom = bbText.y + bbText.height;
    let height0 = wordWraps[0].getBoundingClientRect().height;

    //check max lines and init counter
    let style = window.getComputedStyle(text);
    let maxLines = parseFloat(style.webkitLineClamp);
    let breaks = 0;

    for (let i = 1; i < wordWraps.length; i++) {
      let wordWrap = wordWraps[i];
      wordWrap.classList.remove("wordWrpLine");

      let bb = wordWrap.getBoundingClientRect();
      let height = bb.height;
      let bottomSpan = bb.y + bb.height;

      if (height > height0 && breaks < maxLines - 1 && bottomSpan < bottom) {
        wordWrap.classList.add("wordWrpLine");
        breaks++;
      }
    }
  });
}
* {
  box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100%;
  background: #78b9f3;
  margin: 0;
  font-family: "Arial";
}

.wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
}

.card {
  background: #fff;
  box-shadow: 0 4px 24px 3px rgb(0 0 0 / 10%);
  padding: 20px;
  margin: 20px;
  width: 250px;
  height: auto;
  border-radius: 6px;
  resize: both;
  overflow: hidden;
}

p {
  font-size: 1em;
  line-height: 1.3em;
  margin: 0;
}

h2 {
  font-size: 1.5em;
  margin: 0 0 0.5em 0;
}

.overflow {
  font-size: 20px;
  text-align: right;
  background-color: grey;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
}

.overflow:hover .wordWrp {
  outline: 1px solid red;
}

.maxLines2 {
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.maxLines3 {
  -webkit-line-clamp: 3;
  line-clamp: 3;
}

.maxLines4 {
  -webkit-line-clamp: 4;
  line-clamp: 4;
}

.wordWrpLine {
  word-break: break-word;
}

.wordWrpLine2 {
  background: red;
}
<h2>Resize cards</h2>
<section class="wrapper">
  <div class="card">
    <h2>Fix: 2 lines</h2>
    <p class=" maxLines2 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>

  <div class="card">
    <h2>Fix: 3 lines</h2>
    <p class=" maxLines3 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>
  
  <div class="card">
    <h2>Fix: 4 lines</h2>
    <p class=" maxLines4 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>
  
</section>

示例3: js模拟行截断

这种方法与之前的js方法非常相似-通过将元素文本分割成span元素。

这次我们完全通过添加自定义<span>元素来替换css省略号占位符。

根据某些边界框检查,溢出的“省略号”占位符将被放置:第一个离开溢出定义边界的span元素将是用于插入省略号的位置/索引。

let overflows = document.querySelectorAll(".overflow");
fixOverflow(overflows);

function fixOverflow(overflows) {
  overflows.forEach(function(text) {
    let spanWrps = text.querySelectorAll(".wordWrp");
    let words = '';
    let hasSpans = spanWrps.length ? true : false;
    let spanEllipse = text.querySelector(".spanEllipse");

    //create ellpse san if not already present
    if (!spanEllipse) {
      spanEllipse = document.createElement("span");
      spanEllipse.classList.add('spanEllipse');
      spanEllipse.textContent = ' …';
    }

    // get word/string array - unless it's aleady generated
    if (hasSpans) {
      words = [...spanWrps].map((word) => {
        return word.textContent
      });
    } else {
      //split textContent into array
      words = text.innerHTML
        .split(" ")
        .map((w) => {
          return w.trim();
        })
        .filter(Boolean);
    }


    let bbText = text.getBoundingClientRect();
    let bottomText = bbText.y + bbText.height;

    //delete current textContent
    if (!hasSpans) {
      text.textContent = "";
    }

    for (let i = 0; i < words.length; i++) {
      let word = words[i];

      //wrap words in spans if not already done
      let span = '';
      if (hasSpans) {
        span = spanWrps[i];
        span.classList.remove('wordWrpOverflow');
      } else {
        span = document.createElement("span");
        span.textContent = word + " ";
        text.appendChild(span);
        span.classList.add("wordWrp");
      }

      let bbSpan = span.getBoundingClientRect();
      let bottomSpan = bbSpan.y + bbSpan.height;
      if (bottomSpan > bottomText) {
        span.classList.add("wordWrpOverflow");
      } else {
        span.classList.remove("wordWrpOverflow");
      }
    }

    let firstOverFlow = text.querySelector('.wordWrpOverflow');
    if (firstOverFlow) {
      let bbE = spanEllipse.getBoundingClientRect();
      let bottomE = bbE.y + bbE.height;
      let bbPrev = firstOverFlow.previousElementSibling.getBoundingClientRect();
      let bottomPrev = bbPrev.y + bbPrev.height;

      //add ellipsis before first overflow element
      if (bottomE > bottomText && bottomPrev < bottomText) {
        firstOverFlow.parentNode.insertBefore(spanEllipse, firstOverFlow.previousElementSibling);
      } else {
        // ellipsis might be in overflow area - traverse backwards
        if (bottomPrev > bottomText) {
          firstOverFlow.parentNode.insertBefore(spanEllipse, firstOverFlow.previousElementSibling.previousElementSibling);
        } else {
          firstOverFlow.parentNode.insertBefore(spanEllipse, firstOverFlow);
        }
      }
    }
    // no ellipsis neede - remove it!
    else {
      spanEllipse.remove();
    }
  });
}

//update on resize 
const resizeObserver = new ResizeObserver(() => {
  fixOverflow(overflows)
});
overflows.forEach(function(text, i) {
  resizeObserver.observe(text);
});
* {
  box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100%;
  background: #78b9f3;
  margin: 0;
  font-family: "Arial";
}

.card {
  background: #fff;
  padding: 20px;
  margin: 20px;
  height: auto;
  border-radius: 6px;
  resize: both;
  overflow: auto;
}

p {
  font-size: 1em;
  line-height: 1.3em;
  margin: 0;
}

h2 {
  font-size: 1.5em;
  margin: 0 0 0.5em 0;
}

:root {
  --lineHeight: 1.2em;
}

.overflow {
  font-size: 20px;
  line-height: var(--lineHeight);
  text-align: right;
  overflow: hidden;
}

.maxLines2 {
  max-height: calc(2 * var(--lineHeight));
}

.maxLines3 {
  max-height: calc(3 * var(--lineHeight));
}

.maxLines4 {
  max-height: calc(4 * var(--lineHeight));
}

.wordWrpOverflow {
  visibility: hidden;
}


/* force line break */

.spanEllipse+span {
  display: block
}

.txt-cnt * {
  text-align: center;
}

@media (min-width:720px) {
  .card {
    width: 33%;
  }
  .wrapper {
    display: flex;
    justify-content: center;
  }
}

@media (min-width:1024px) {
  .card {
    width: 250px;
  }
}
<section class="wrapper">


  <div class="card">
    <h2>Fix: 2 lines</h2>
    <p class=" maxLines2 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>


  <div class="card">
    <h2>Fix: 3 lines</h2>
    <p class=" maxLines3 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>

  <div class="card txt-cnt">
    <h2>Fix: 4 lines: <br />text-align:center</h2>
    <p class=" maxLines4 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>

</section>

排除支持proper text-overflow/line-clamp的浏览器

目前只有Firefox和某些iOS Safari版本似乎可以正确地渲染省略号。
为了避免这些浏览器进行不必要的js处理,您可以包含一个特性检测:

let firefoxOverflow = CSS.supports("text-overflow", "ellipsis ellipsis");
let safariWebkit = CSS.supports("-webkit-hyphens", "none");
let needsWorkaround = false;
if(!firefoxOverflow && !safariWebkit) {
  needsWorkaround = true;
  document.body.classList.add("needs-overflow-fix");
}

这肯定不适用于所有的火狐和Safari版本。
至少它没有使用任何浏览器嗅探。

/**
 * check proper overflow support
 **/
let supportText = "";
let firefoxOverflow = CSS.supports("text-overflow", "ellipsis ellipsis");
let safariWebkit = CSS.supports("-webkit-hyphens", "none");
let needsWorkaround = false;
if (!firefoxOverflow && !safariWebkit) {
  needsWorkaround = true;
  document.body.classList.add("needs-overflow-fix");
}

// just for display
if (needsWorkaround) {
  supportText = "needs workaround - use js";
} else {
  supportText = "proper support – use css";
}
support.textContent = supportText;


/**
* apply workaround if necessary
**/
if (needsWorkaround) {
  let overflows = document.querySelectorAll(".overflow");
  fixOverflow(overflows);

  function fixOverflow(overflows) {
    overflows.forEach(function (text) {
      
      /*
      let style = window.getComputedStyle(text);
      let styleClamp = parseFloat(style.getPropertyValue("-webkit-line-clamp"));
      styleClamp = !styleClamp
        ? parseFloat(style.getPropertyValue("line-clamp"))
        : styleClamp;
      //console.log(style)
      console.log("clamp", styleClamp);

      if (styleClamp > 0) {
        //text.classList.add('maxLines'+styleClamp);
      }
      */

      let spanWrps = text.querySelectorAll(".wordWrp");
      let words = "";
      let hasSpans = spanWrps.length ? true : false;
      let spanEllipse = text.querySelector(".spanEllipse");

      //create ellpse san if not already present
      if (!spanEllipse) {
        spanEllipse = document.createElement("span");
        spanEllipse.classList.add("spanEllipse");
        spanEllipse.textContent = " …";
      }

      // get word/string array - unless it's aleady generated
      if (hasSpans) {
        words = [...spanWrps].map((word) => {
          return word.textContent;
        });
      } else {
        //split textContent into array
        words = text.innerHTML
          .split(" ")
          .map((w) => {
            return w.trim();
          })
          .filter(Boolean);
      }

      let bbText = text.getBoundingClientRect();
      let bottomText = bbText.y + bbText.height;

      //delete current textContent
      if (!hasSpans) {
        text.textContent = "";
      }

      for (let i = 0; i < words.length; i++) {
        let word = words[i];

        //wrap words in spans if not already done
        let span = "";
        if (hasSpans) {
          span = spanWrps[i];
          span.classList.remove("wordWrpOverflow");
        } else {
          span = document.createElement("span");
          span.textContent = word + " ";
          text.appendChild(span);
          span.classList.add("wordWrp");
        }

        let bbSpan = span.getBoundingClientRect();
        let bottomSpan = bbSpan.y + bbSpan.height;
        if (bottomSpan > bottomText) {
          span.classList.add("wordWrpOverflow");
        } else {
          span.classList.remove("wordWrpOverflow");
        }
      }

      let firstOverFlow = text.querySelector(".wordWrpOverflow");
      if (firstOverFlow) {
        let bbE = spanEllipse.getBoundingClientRect();
        let bottomE = bbE.y + bbE.height;
        let bbPrev = firstOverFlow.previousElementSibling.getBoundingClientRect();
        let bottomPrev = bbPrev.y + bbPrev.height;

        //add ellipsis before first overflow element
        if (bottomE > bottomText && bottomPrev < bottomText) {
          firstOverFlow.parentNode.insertBefore(
            spanEllipse,
            firstOverFlow.previousElementSibling
          );
        } else {
          // ellipsis might be in overflow area - traverse backwards
          if (bottomPrev > bottomText) {
            firstOverFlow.parentNode.insertBefore(
              spanEllipse,
              firstOverFlow.previousElementSibling.previousElementSibling
            );
          } else {
            firstOverFlow.parentNode.insertBefore(spanEllipse, firstOverFlow);
          }
        }
      }
      // no ellipsis neede - remove it!
      else {
        spanEllipse.remove();
      }
    });
  }

  //update on resize
  const resizeObserver = new ResizeObserver(() => {
    fixOverflow(overflows);
  });
  overflows.forEach(function (text, i) {
    resizeObserver.observe(text);
  });
}
* {
  box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100%;
  background: #78b9f3;
  margin: 0;
  font-family: "Arial";
}

.needs-overflow-fix {
  background: orange;
}

.card {
  background: #fff;
  padding: 20px;
  margin: 20px;
  height: auto;
  border-radius: 6px;
  resize: both;
  overflow: auto;
}

p {
  font-size: 1em;
  line-height: 1.3em;
  margin: 0;
}

h2 {
  font-size: 1.5em;
  margin: 0 0 0.5em 0;
}

:root {
  --lineHeight: 1.2em;
}

.overflow {
  font-size: 20px;
  line-height: var(--lineHeight);
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
}

.needs-overflow-fix .overflow {
  text-overflow: unset;
  overflow: hidden;
  display: block;
  -webkit-line-clamp: unset !important;
  line-clamp: unset !important;
  -webkit-box-orient: unset !important;
}

.maxLines2 {
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.maxLines3 {
  -webkit-line-clamp: 3;
  line-clamp: 3;
}

.maxLines4 {
  -webkit-line-clamp: 4;
  line-clamp: 4;
}

.needs-overflow-fix .maxLines2 {
  max-height: calc(2 * var(--lineHeight));
}
.needs-overflow-fix .maxLines3 {
  max-height: calc(3 * var(--lineHeight));
}
.needs-overflow-fix .maxLines4 {
  max-height: calc(4 * var(--lineHeight));
}

.wordWrpOverflow {
  visibility: hidden;
}

/* force line break */
.spanEllipse + span {
  display: block;
}

.txt-cnt * {
  text-align: center;
}

@media (min-width: 720px) {
  .card {
    width: 33%;
  }

  .wrapper {
    display: flex;
    justify-content: center;
  }
}
@media (min-width: 1024px) {
  .card {
    width: 250px;
  }
}
<p style="text-align:center"><strong id="support" >&nbsp;</strong></p>
<section class="wrapper">
  <div class="card">
    <h2>Fix: 2 lines</h2>
    <p class=" maxLines2 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>
  <div class="card">
    <h2>Fix: 3 lines</h2>
    <p class=" maxLines3 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>
  <div class="card txt-cnt">
    <h2>Fix: 4 lines: <br />text-align:center</h2>
    <p class="maxLines4 overflow">Hello, I'm a very long text1 for at least three lines! Hello, I'm a very long text2 for at least three lines! Hello, I'm a very long text2 for at least three lines!</p>
  </div>

</section>

如果背景颜色从蓝色变为橙色-应用JS修复。


很酷的例子!我只是想知道,为什么对于“text-align: left”,省略号的行为符合预期,但对于“center”和“right”,它却出现了问题。这是 Webkit 样式实现中的一个 bug 吗? - Sinigami
@Sinigami:我认为这更像是 Chromium 浏览器基于实现的不足,而非一个 bug;值得注意的是:在 Firefox 和 Chromium 浏览器上比较上述的 MDN 文档页面已经显示了渲染不一致性 - 在 Chromium 中,你甚至不能省略或更改溢出字符串以及类似“... 阅读更多”的内容。因此,简而言之 - 无论是 text-overflow 还是 line-clamp 都不是实现可预测跨浏览器呈现的“万无一失”的概念。 - herrstrietzel

2

演示:去年六月份,我在Codepen上创建了一个小教程,比较了水平省略号和垂直线夹在一起的效果,包括响应式大小和间距以及一些可以玩耍的滑块。演示中的内联JS仅用于控制自定义变量,与您的问题无关。我为您的目的添加了text-align单选按钮。

解决方案:通过将我的代码与您的代码进行比较,我认为您只需要将您的text-overflow: ellipsis;(在'good'或'bad'中使用)更改为overflow: hidden,根据需要设置text-align即可。

direction属性的值仅与text-align的值相关。例如:

  • body[dir="ltr"] p { text-align: right }
  • body[dir="rtl"] p { text-align: left }

但这在HTML文档中是相当常见的做法,似乎并不是问题所在。

下面的片段展示了您需要使用自定义属性line-clamp来进行行夹紧的必要CSS。

如果您需要更详细的解释,请在评论中告诉我。

[line-clamp] {
  /* Alternative character not supported
     by Edge/Chrome and line-clamp */

/* All [MANDATORY] for vertical line clamp */
  width: 100%;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;

}

.test {
  text-align: right
}
<p class="test" line-clamp>Lorem Ipsum is simply some dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not
  only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software
  like Aldus PageMaker including versions of Lorem Ipsum.
</p>


抱歉,但是当我运行您的代码片段时(在Chrome浏览器中),我没有看到省略号。https://pasteboard.co/Sb13KbaHBx51.png - Sinigami
我刚刚测试了你提到的Chrome问题。问题似乎是Stackoverflow特定的,只在某些断词情况和视口宽度下发生。代码本身在Chrome/Edge之外的地方运行得非常好,Codepen链接也是如此。您可以通过在Firefox / Chrome / Edge中玩弄Codepen中的滑块来自行验证。至于iOS应用程序,我不清楚。据我所知,除了遵循MDN:-webkit-line-clamp规范之外,不需要进行任何特殊编码... - Rene van der Lende

2

我刚刚发现了一个解决上述问题的诀窍:

  1. 为 div/p 添加 padding-right: 20px --> 这个数字是硬编码,只显示三个点
  2. 使用 text-align: right

希望这种方法能够帮助您暂时解决问题。

请参见下面的示例:

* {
  box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #78b9f3;
  margin:0;
  font-family: "Arial";
}

.wrapper {
  display: flex;
  justify-content: center;
}

.card {
  background: #fff;
  box-shadow: 0 4px 24px 3px rgb(0 0 0 / 10%);
  padding: 20px;
  margin: 20px;
  width: 200px;
  height: auto;
  border-radius: 6px;
}

p {
  font-size: 1em;
  line-height: 1.3em;
  margin:0;
}

h2 {
  font-size: 1.5em;
  margin: 0 0 0.5em 0;
}

.good {
  font-size: 20px;
  background-color: grey;
   text-overflow: ellipsis;
   display: -webkit-box;
   -webkit-line-clamp: 2;
           line-clamp: 2; 
   -webkit-box-orient: vertical;
   
   text-align: right;
   padding-right: 20px; //added code
  
}

.bad {
  font-size: 20px;
  text-align: right;
  background-color: grey;
   text-overflow: ellipsis;
   display: -webkit-box;
   -webkit-line-clamp: 2;
           line-clamp: 2; 
   -webkit-box-orient: vertical;
}
<section class="wrapper">
  <div class="card">
    <h2>Good</h2>
    <p class="good">Hello, I'm a very long text for at least three lines!</p>
  </div>
  <div class="card">
    <h2>Bad</h2>
    <p class="bad">Hello, I'm a very long text for at least three lines!</p>
  </div>


</section>


1
如前所述,基于Chromium的浏览器不会按预期实现text-overflow: ellipsis;text-align: right;的结合使用,因此不可避免地需要使用JavaScript解决方案。
如果段落中不包含HTML,而只包含文本(如您的示例中所示),并且已知可能出现的空格字符位置(请参见下面的getLinebreakPosis),则此答案有效。但是,在此示例中,似乎仅出现平均空格字符。
此外,如果您处理非常长的单词,则还需要添加连字符库,这将使事情变得棘手。

function exceedsHeight(element, height) {
  return parseFloat(getComputedStyle(element).height) > height
}

function refillParagraph(paragraph, maxHeight=42, endingString=' ...') {
// If text overflows maxHeight, cut it off and add an endingString.

  // If paragraph is not overflowing, do nothing and abort:
  if( ! exceedsHeight(paragraph, maxHeight) ) return

  // Otherwise store text and remove it of paragraph:
  const text = paragraph.textContent.trim()
  paragraph.textContent = ''

  // Refill paragraph character by character, plus the endingString:
  for(let i=0; i < text.length; i++) {
    paragraph.textContent = text.slice(0, i) + endingString

    // Paragraph overflows:
    if(exceedsHeight(paragraph, maxHeight)) {

      // Remove last character and get breaking points:
      let posis = getLinebreakPosis( text.slice(0, i-1) )

      // Fill paragraph with text until last found breaking point,
      // plus the endingString:
      for(let j=0; j < posis.length; j++) {
        paragraph.textContent = text.slice(0, posis[j]) + endingString

        // No overflow, found breaking point, break posis-loop:
        if( ! exceedsHeight(paragraph, maxHeight) ) break
      }
      // We're done, break the for-each-character-of-text-loop:
      break
    }
  }
}

function getLinebreakPosis(text) {
  // Return array with all positions where a space occurs in the text.
  // Sort by last found first.
  const posis = []
  const spaceChars = [' ', '\n', '\t'] // and probably some more
  let i = text.length
  while(i > 0) {
    i--
    if(spaceChars.includes(text[i])) {
      posis.push(i)
    }
  }
  return posis
}

// For every found paragraph, execute refillParagraph:
document.addEventListener('DOMContentLoaded', eve => {
  document.querySelectorAll('p').forEach(p => refillParagraph(p) )
});
p {
  width: 200px;
  text-align: right;
}
<article>
  <h2>Ellipse all the browsers</h2>
  <p>
    This is a paragraph with a decent amount of text in it, for illustration purposes.
  </p>
  <p>
    This is another paragraph which could be filled with ancient latin texts, just to exclude the most of us.
  </p>
</article>


0
现在看起来这个问题似乎只在Chrome中出现... Firefox和Safari可以正确地渲染多行截断文本后的...,即使使用了text-align: right。 只有Chrome以一种奇怪的“绝对定位”的方式将省略号渲染在容器元素之外。
我找到的解决Chrome的方法是在截断文本元素上添加padding-right: 1em

p {
  width: 300px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  text-align: right;
  padding-right: 1em; 
}
<p>
  In this example the <code>-webkit-line-clamp</code> property is set to
  <code>3</code>, which means the text is clamped after three lines. An ellipsis
  will be shown at the point where the text is clamped.
</p>


-2
  • 您需要使用 direction: rtl; 来设置浏览器更改方向。
  • 并且使用 text-align:right,这样也会出现在右侧以符合预期行为。

* {
  box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #78b9f3;
  margin: 0;
  font-family: "Arial";
}

.wrapper {
  display: flex;
  justify-content: center;
}

.card {
  background: #fff;
  box-shadow: 0 4px 24px 3px rgb(0 0 0 / 10%);
  padding: 20px;
  margin: 20px;
  width: 200px;
  height: auto;
  border-radius: 6px;
}

p {
  font-size: 1em;
  line-height: 1.3em;
  margin: 0;
}

h2 {
  font-size: 1.5em;
  margin: 0 0 0.5em 0;
}

.good {
  font-size: 20px;
  background-color: grey;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
}

.bad {
  font-size: 20px;
  text-align: right;
  background-color: grey;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  direction: rtl;
}
<h1> IN that case to bring ... to right you have to set <em>direction: rtl;</em></h1>
<section class="wrapper">
  <div class="card">
    <h2>Good</h2>
    <p class="good">Hello, I'm a very long text for at least three lines!</p>
  </div>
  <div class="card">
    <h2>Bad</h2>
    <p class="bad">Hello, I'm a very long text for at least three lines!</p>
  </div>


</section>


看,tree dot现在在左侧...它们应该在右侧... - Stetho
text-align:right,因此点号也应在右侧。 - Nexo
该样式已经具有此属性,请查看您的代码片段运行:https://pasteboard.co/RheXnP57bFZ7.png - Stetho
方向:rtl;是否存在? - Nexo
请查看我在之前评论中提供给您的屏幕截图。在您的示例中,省略号位于文本左侧 - 这不是我想要的目标行为。所以:1)文本应该对齐右侧2)省略号应该对齐右侧。 - Stetho

-2

我已经将 direction rtl 添加到 bad class 中,点已经翻转,这正是您需要的。

working image

同时,我还添加了overflow hidden来隐藏文本的其余部分。希望这能对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接