Changing text alignment for each line in multi-lin

2019-09-02 06:15发布

In old newspapers, titles of articles would, if they stretched over three lines, often be centered like this (picture):

|RACKET KINGPINS       |
|    ARE ROUNDED UP    |
|       IN BOMB KILLING|

I.e. the alignment for the first line is left, second center, third right. Similarly, for titles that stretched over two lines, the first would be left-aligned and the second right-aligned. For one-liners, the title would be centered.

I've been trying to emulate this for a theme I'm working on but the problem is, how do I separate the lines from each other? The title for each post does of course not contain any actual hard-coded line breaks but break automatically according to the width constraints, so I can't just look for line breaks to split up the innerHTML of the DOM, for example.

So how would you go about doing this?

(I welcome a JavaScript solution, if that was unclear)

3条回答
Viruses.
2楼-- · 2019-09-02 06:26

You can try looping through all the characters, in each loop, select the current character into a range object, from that you can use getBoundingClientRect() method of the range object to get the bottom of the character rect. It's the way we detect the ending character on each line. Here is the signs to detect the ending character:

  1. If we use normal wrapping, all the lines should break at some space character. The special thing is that in this case, the ending space character will have rect (getting from getBoundingClientRect()) with both top and bottom being 0.
  2. If we use wrapping like word-break:break-all, the ending character may be some other character (not the space character). In this case we will detect the next character following the ending character, the next character will have bottom different from the ending character's bottom.

So it's fairly complicated indeed. In fact at first I thought we had only 1 case (the second case) but if we ignore the first case, we will have unexpected result. So firstly we need to check the first case, then the second case. Now detecting the ending character in each line helps us separate the whole text into separate lines. Then we can wrap each line in a div element with appropritate style set (to align the text in each line).

Here is the demo's code:

HTML:

<h3>Original column</h3>
<div class='o-column'>racket kingpins are rounded up in bomb killing</div>
<h3>Column after adjusted</h3>
<div class='column'>racket kingpins are rounded up in bomb killing</div>

CSS:

.column, .o-column {
  width:300px;  
  border:1px solid black;
  margin-bottom:5px;
  text-transform:uppercase;
  padding:5px;    
  font-size:30px;
  font-family:'Arial';
  word-wrap:break-word;
}

JS:

console.clear();
var column = document.querySelector('.column');
var ends = [];
var range = document.createRange();
range.selectNodeContents(column);
var lines = [];
var lastBottom = null;
//if the innerHTML has some \n character, there will be some unexpected
//behavior, all the \n characters should be removed first.
column.innerHTML = column.innerHTML.replace(/\n/g,'');
var len = column.innerHTML.length;
for(var i = 0; i < len; i++){    
  //set range for current character
  range.setStart(column.childNodes[0],i);
  range.setEnd(column.childNodes[0],i+1);
  var rect = range.getBoundingClientRect();    
  if((rect.bottom == 0 && rect.top == 0) ||
    rect.bottom != lastBottom && lastBottom != null || i == len-1){
    var line = document.createElement('div');
    var lineStart = ends.length ? ends[ends.length - 1] + 1 : 0;
    line.innerHTML = column.innerHTML.substring(lineStart, i == len - 1 ? len : i);
    lines.push(line);
    ends.push(rect.bottom ? i - 1 : i);    
  }
  if(rect.bottom != 0) lastBottom = rect.bottom;
  else lastBottom = null;
  if(ends.length > 3) break;
}

var n = lines.length;
//we align each line only if there are less than 4 lines
if(n < 4 && n > 0){
  column.innerHTML = "";    
  for(var i = 0; i < n; i++){
    column.appendChild(lines[i]);
  }
  if(n == 1) lines[0].style.textAlign = 'center';
  else {        
    lines[0].style.textAlign = 'left';        
    lines[n-1].style.textAlign = 'right';
    if(n == 3) lines[1].style.textAlign = 'center';
  }
}

Demo.

查看更多
3楼-- · 2019-09-02 06:33

Without JavaScript, you can't.

This is one of the limitations of HTML/CSS. If you need printing capabilities where you control exact position and size of text, use PDF.

Without JavaScript, you can't predict if a paragraph will span multiple lines and how much, simply because a user:

  • May not have the same fonts installed,

  • Or may have configured the text to appear bigger in his browser.

You can, instead:

  1. Add manually the line breaks in a way that in most situations, the width of every line will be inferior to the width of the container,

  2. Forbid line breaks (white-space: nowrap;) and:

  3. Align the three lines according to your needs.

With JavaScript, you may determine where to put line breaks by counting the actual number of lines, and then reducing the text character by character, until you get two lines, then one line, finding this way the exact locations where you may insert line breaks.

I consider it hackish enough, but, well, it's just my opinion. You may, on the other hand, care that changes to user configuration (such as text-only zoom like the one which was used in old browsers) once the page is loaded may break the layout.

查看更多
叼着烟拽天下
4楼-- · 2019-09-02 06:33

You could create a function to explode the string into an array of chars in javascript and then manipulate the array as needed.

You could make he manipulation easier by using a pr-made "word wrap" function on the string prior to exploding it, so that the title will automatically have \n characters inserted appropriately beforehand according to your max width specification.

Then after exploding you just need to wrap the sections in divs by inserting them in the array at the \n break points and use css classes to line them up as needed.

Then implode the array back into a string...

查看更多
登录 后发表回答