Village Village - 4 months ago 4
CSS Question

How to center a paragraph of text on a particular word in HTML, CSS, or JavaScript?

I have a passage that is centered on the page, like this:

_________________________
| |
| Once upon a time, there |
| lived a squirrel who |
| lived in the middle of |
| the forest. |
|_________________________|


I need to adjust the centering such that a specifically marked word is horizontally centered on the page. In this example, the word "who" is centered:

_________________________
| |
| Once upon |
| a time, there lived a |
| squirrel who lived in |
| the middle of the |
| forest. |
|_________________________|



  • The word who is marked in the HTML with a
    <div>
    tag, i.e.
    <center>Once upon a time, there lived a squirrel <div style="centered">who</div> lived in the middle of the forest.</center>
    .

  • The HTML appears in an application in which I have little control over editing the HTML itself (the text is already marked with CSS style tags), but can modify the style sheet or add code, such as JavaScript to the header to modify how that style is rendered.



How can I center a passage on a particular word in HTML, CSS, or JavaScript?

Answer

shift a particular target word to the best horizontal center that words will allow

You can achieve a rough centering using quite simple JavaScript. All it does is scan along the mark-up, converts the first TextNode it finds to "words" wrapped with <span>s, and then trials and errors inserting line breaks until it finds the one that will get the target word nearest to the center.

In the fiddle I have highlighted in red the spans that the code deems should have a break after, and green for the target word. In the most cases it does quite well, there are a few occurrences where you may end up with a single word on a line — however, this could be fixed by tweaking the code to your exact use-case.

Just in case people aren't aware, this is an illustration of what can be achieved and is by no means perfect — no code ever is, because it can always be improved, and should be.

fiddle

http://jsfiddle.net/s8XgJ/2/

updated fiddle that shows behaviour with a random targeted word:

http://jsfiddle.net/s8XgJ/3/

markup

<center>
  Once upon a time, there was a squirrel who lived in the 
  <span class="centered">middle</span> of the forest. I 
  say middle, but without knowing where the edges were, 
  the squirrel had no idea where the center was.
</center>

I have kept your mark-up roughly, however, as others have stated, you should avoid using the center tag. I have also converted the inner div to a span to preserve inline spacing. If this is not possible for you, you can still use a div, you'll just have to override it's normal display to be display: inline-block;

JavaScript / jQuery

jQuery.fn.findYourCenter = function( target ){
  base = $(this); target = base.children(target);
  base.contents().eq(0).each(function(){
    var i = -1, word, words, found, dif,
      last = Infinity,
      node = $(this), 
      text = node.text().split(/\s+/),
      cwidth = Math.round(target.width() * 0.5),
      bwidth = Math.round(base.width() * 0.5),
      breaks = 2 // code will try to insert 2 line breaks
    ;
    node.replaceWith(
      '<span class="word">'+text.join(' </span><span class="word">')+' </span>'
    );
    words = base.find('.word');
    do {
      while ( ++i < words.length ){
        word && word.removeClass('clear');
        word = words.eq(i).addClass('clear');
        dif = Math.round(Math.abs((target.position().left + cwidth) - bwidth));
        if ( dif < last ) { last = dif; found = i; }
      }
      word.removeClass('clear');
      if ( found ) {
        words.eq(found).addClass('clear');
        i = found; found = word = null;
      }
      else {
        break;
      }
    } while ( i < words.length && --breaks );
  });
};

I have used jQuery for brevity.

CSS

You'll require this CSS item:

.clear:after {
  content: '';
  display: block;
  clear: both;
}

and perhaps the following if your .centered item is a <div>:

.centered {
  display: inline-block;
}

Then all you need execute is:

jQuery(function($){
  $('center').findYourCenter('.centered');
});


problems

To get things precise you'll have to offset something — due to the fact that depending on the length (and placement of) words you cannot always achieve perfect center for paragraph, line and target word all at the same time. The above code still keeps the line centered, at the cost of the target word being offset. You could improve the above to make margin modifications to the line that holds the target span, if you wanted to center the word at the cost of the line.

Currently, if the target word appears in the first say five words, centering is unlikely to be achieved, basically because this method relies on word-wrap and line-breaks. If neither can be used due to the word being at the beginning of the paragraph, then nothing can be done — save for introducing margin, padding or text-indent.

This code relies on being able to read the position of <span> elements correctly. Older browsers e.g. IE6 and early versions of Safari/Opera have had problems with this.

One further thing to note. This code relies on the browser recalculating it's internal measurements immediately in the same synchronous execution loop. Most modern browsers seem to do this — in most cases — however you may find you need to implement setTimeout(func, 0) or setImmediate delays in order for this to work on older systems.


... and finally

This is a rather bonkers request, what exactly is it for? :)

Comments