如何动态更改标题的宽度以确保行长均匀

How to dynamically change width of a headline to ensure even line lengths

我想弄清楚 The Washington Post website 如何强制主标题如此漂亮地中断,而不管实际文本或屏幕宽度如何。据我所知,如果 h1 的 class 为 headline,则动态计算其宽度以达到效果。我知道这是用 JavaScript 完成的,并且想象它不是 hard-coded 那么实际计算的宽度是如何以某种方式认识到标题将如何中断(防止高度可变的行长度、孤儿等)。我只是无法找到控制此功能的 JS。

这是正常加载页面的示例:

以及删除了 headline class 的示例:

(您也可以在页面加载时注意到差异,大概是在 JavaScript 开始之前。)

谢谢!

这就是你想要的。显然,因为有些单词比其他单词长,所以并非所有行的长度都完全相同。下面的代码保留了浏览器创建的 数量 换行符。然后,它调整换行符的位置以确保 或多或少 均匀的行长度。

P.S.: 尝试 运行 下面没有 JS 的代码,你会看到区别

let h1 = document.querySelector('h1')
let text = h1.textContent
let splitText = text.split(' ')
let getContentBoxHeight = elem => {
  let elemStyle = window.getComputedStyle(elem)
  let elemHeightWithPadding = parseInt(elemStyle.getPropertyValue('height'))
  let elemPadding = parseInt(elemStyle.getPropertyValue('padding-top')) + parseInt(elemStyle.getPropertyValue('padding-bottom'))
  return elemHeightWithPadding - elemPadding
}
let getContentBoxWidth = elem => {
  let elemStyle = window.getComputedStyle(elem)
  let elemWidthWithPadding = parseInt(elemStyle.getPropertyValue('width'))
  let elemPadding = parseInt(elemStyle.getPropertyValue('padding-left')) + parseInt(elemStyle.getPropertyValue('padding-right'))
  return elemWidthWithPadding - elemPadding
}

// return the number of line breaks created by the browser
let breakPointAmount = (() => {
  let body = document.querySelector('body')
  let dummyH1 = document.createElement('h1')
  let oneLineHeight
  let totalLineHeight = getContentBoxHeight(h1)

  dummyH1.appendChild(document.createTextNode('M'))
  body.appendChild(dummyH1)
  oneLineHeight = getContentBoxHeight(dummyH1)
  dummyH1.remove()
  return Math.round(totalLineHeight / oneLineHeight) - 1
})()

// refine the number of line breaks created by the browser
let breakPoints = (() => {
  let denominator = breakPointAmount + 1
  let points = []
  let h1Length
  h1.style.width = 'max-content'
  h1Length = getContentBoxWidth(h1)
  h1.style.width = ''
  for (let i = 0; i < breakPointAmount; i++) {
    points.push(Math.round(h1Length * (i + 1) / denominator))
  }
  return points
})()

// determine where that same number of break points should go in text
let indexesToBreak = Array(breakPointAmount).fill(1)
let cumulativeLength = 0
let cumulativeText = ''
for (let i = 0; i < splitText.length; i++) {
  let word = splitText[i]
  let calculateLength = word => {
    let body = document.querySelector('body')
    let dummyH1 = document.createElement('h1')
    let length
    dummyH1.appendChild(document.createTextNode(word))
    dummyH1.style.width = 'max-content'
    body.appendChild(dummyH1)
    length = getContentBoxWidth(dummyH1)
    dummyH1.remove()
    return length
  }

  cumulativeText += word + ' '
  cumulativeLength = calculateLength(cumulativeText)

  for (let j = 0; j < indexesToBreak.length; j++) {
    if (indexesToBreak[j] === 1) {
      indexesToBreak[j] = {
        index: i,
        currentMin: Math.abs(cumulativeLength - breakPoints[j])
      }
    } else {
      if (cumulativeLength - breakPoints[j] < indexesToBreak[j].currentMin) {
        indexesToBreak[j] = {
          index: i,
          currentMin: Math.abs(cumulativeLength - breakPoints[j])
        }
      }
    }
  }
}

// insert break points at updated locations into text
let newText = (function() {
  let final = ''
  let itbIndex = 0
  for (let i = 0; i < splitText.length; i++) {
    final += `${splitText[i]} `
    if (indexesToBreak[itbIndex] && i === indexesToBreak[itbIndex].index) {
      final += '<br>'
      itbIndex += 1
    }
  }
  return final.trim()
})()

// add text with new break points to page
h1.innerHTML = newText
h1 {
  text-align: center;
}
<h1>Some Long Title that Requires Line Breaking To Ensure Even Line Lengths</h1>