How to center a paragraph of text on a particular

2020-02-27 07:24发布

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?

16条回答
仙女界的扛把子
2楼-- · 2020-02-27 08:02

[EDIT] Added fiddle. Cleaned up some code. Updated snippet below. Added comments.

http://jsfiddle.net/2Hgur/

[ORIGINAL] I don't believe that this is possible with css. However, I figured a solution could be possible with JavaScript. So, I tried it, and it worked! While this might not be the ultimate solution, consider the following:

<div class="jsCenterOnPhrase">

The quick brown fox jumped over the lazy dog.
The quick brown fox jumped over the <div class="jsCenteredPhrase">lazy dog.</div>
The quick brown fox jumped over the lazy dog.
The quick brown fox jumped over the lazy dog.
The quick brown fox jumped over the lazy dog.

</div>

Then break it down and rebuild it, perhaps like this:

jQuery(function ($) {

var $divParents = $('.jsCenterOnPhrase'); // all instances
var sMarker = '##'; // marker for phrase position

// for each instance
$divParents.each(function () {

    // closures
    var $parent = $(this);
    var $phrase = $parent.find('div.jsCenteredPhrase');

    // if there is only one phrase
    if ($phrase.size() == 1) {

        // more closures
        var iParentWidth = $parent.innerWidth();
        var sPhrase = $phrase.text();
        var iPhraseWidth = $phrase.outerWidth();
        $phrase.replaceWith(sMarker);
        var sFullText = $parent.text().replace(/\s+/g, ' ');
        // add html structure to container
        $parent.html(
            '<span class="jsLeftWrap"></span>' +
            '<span class="jsCenterWrap">' +
            '<span class="jsCenterPrefix"></span>' +
            '<span class="jsCenterPhrase"></span>' +
            '<span class="jsCenterSuffix"></span>' +
            '</span>' +
            '<span class="jsRightWrap"></span>');
        // more closures
        var $LeftWrap = $parent.find('.jsLeftWrap');
        var $CenterWrap = $parent.find('.jsCenterWrap');
        var $CenterPrefix = $parent.find('.jsCenterPrefix');
        var $CenterPhrase = $parent.find('.jsCenterPhrase');
        var $CenterSuffix = $parent.find('.jsCenterSuffix');
        var $RightWrap = $parent.find('.jsRightWrap');

        // get all the words left and right into arrays
        var iLeftStart = 0;
        var iLeftEnd = sFullText.indexOf(sMarker);
        var iRightStart = iLeftEnd + sMarker.length;
        var iRightEnd = sFullText.length - 1;
        var sFullLeft = sFullText.substring(iLeftStart, iLeftEnd);
        var sFullRight = sFullText.substring(iRightStart, iRightEnd);
        var aFullLeft = sFullLeft.split(' ');
        var aFullRight = sFullRight.split(' ');

        // build out each word as a node
        for (var i = 0; i < aFullLeft.length; i++) {
            var sVal = aFullLeft[i];
            if (sVal != '') {
                $('<span> ' + sVal + ' </span>').appendTo($LeftWrap);
            }
        }
        for (var i = 0; i < aFullRight.length; i++) {
            var sVal = aFullRight[i];
            if (sVal != '') {
                $('<span> ' + sVal + ' </span>').appendTo($RightWrap);
            }
        }

        // reset text as full html words
        sFullLeft = $LeftWrap.html();
        sFullRight = $RightWrap.html();

        var fResize = function () {
            // reset closures for dynamic sizing
            $LeftWrap.html(sFullLeft);
            $CenterPrefix.html('').css('padding-left', 0);
            $CenterPhrase.html('&nbsp;' + sPhrase + '&nbsp;');
            $CenterSuffix.html('').css('padding-right', 0);
            $RightWrap.html(sFullRight);
            iParentWidth = $parent.innerWidth();

            // private variables
            var $leftWords = $LeftWrap.find('span');
            var $rightWords = $RightWrap.find('span');
            var iMaxWidthRemaining = (iParentWidth - $CenterPhrase.outerWidth());
            var iMaxSideWidth = Math.floor(iMaxWidthRemaining / 2);
            var iLeftRemaining = iMaxSideWidth;
            var iRightRemaining = iMaxSideWidth;
            var iCurrentWidth = iPhraseWidth;
            var iLeftIndex = $leftWords.size() - 1; // work backwards
            var iRightIndex = 0; // work forwards
            var iKerningTollerance = 2; // wraps too much sometimes

            // loop trackers
            var bKeepGoing = true;
            var bKeepGoingRight = true;
            var bKeepGoingLeft = true;

            // loop while there is still room for the next word
            while (bKeepGoing) {

                // check the left side
                if (bKeepGoingLeft) {
                    // get the next left word
                    var $nextLeft = $leftWords.eq(iLeftIndex);
                    var iLeftWordWidth = $nextLeft.outerWidth();
                    if (iLeftWordWidth < iLeftRemaining) {
                        // there's enough room.  add it.
                        $nextLeft.prependTo($CenterPrefix);
                        iLeftRemaining = iMaxSideWidth - $CenterPrefix.outerWidth();
                        iLeftIndex--;
                    } else {
                        // not enough room.  add remaining room as padding
                        bKeepGoingLeft = false;
                        $CenterPrefix.css('padding-left', (iLeftRemaining - iKerningTollerance));
                        iLeftRemaining = 0;
                    }
                }

                // check the right side
                if (bKeepGoingRight) {
                    // get the next right word
                    var $nextRight = $rightWords.eq(iRightIndex);
                    var iRightWordWidth = $nextRight.outerWidth();
                    if (iRightWordWidth < iRightRemaining) {
                        // there's enough room.  add it.
                        $nextRight.appendTo($CenterSuffix);
                        iRightRemaining = iMaxSideWidth - $CenterSuffix.outerWidth();
                        iRightIndex++;
                    } else {
                        // not enough room.  add remaining room as padding
                        bKeepGoingRight = false;
                        $CenterSuffix.css('padding-right', (iRightRemaining - iKerningTollerance));
                        iRightRemaining = 0;
                    }
                }

                // is there room left on either side?
                bKeepGoing = bKeepGoingLeft || bKeepGoingRight;
            }

            // calculate where to put the breaks.
            var iTempWidth = iParentWidth;
            while (iLeftIndex > 0) {
                var $word = $leftWords.eq(iLeftIndex);
                iTempWidth = iTempWidth - $word.outerWidth();
                // account for kerning inconsistencies
                if (iTempWidth <= (2 * iKerningTollerance)) {
                    $('<br />').insertAfter($word);
                    iTempWidth = iParentWidth;
                } else {
                    iLeftIndex--;
                }
            }

        };

        // initial state
        fResize();

        $(window).resize(fResize);
    }
});

});

[ UPDATED FOR REQUIRED NAMING CONVENTION... style="centered" :) ]

查看更多
狗以群分
3楼-- · 2020-02-27 08:05

The center tag is deprecated and not supported in HTML5, so do not use it. Instead give your .centered class some CSS rules like this:

.centered { 
    clear: both;
    display: block;
    text-align: center;
}

However, this will give its own line. If you still want the text around it to wrap, then you'll have to systematically choose your line-breaks – there is no current way to wrap text around a centered node, and float: center doesn't exist…

查看更多
甜甜的少女心
4楼-- · 2020-02-27 08:07

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? :)

查看更多
你好瞎i
5楼-- · 2020-02-27 08:08

As other pointed out its not possible to do through html/Css which I agree too, but you can do some sort of centering based on a line(sentence) rather then by a word. Hope this helps you !!

CSS:

.centered  { display:inline-block; text-align:center;}
div {  width:100%; text-align:center; }

HTML :

 <div>  <div class="centered"> Once upon a time, </div> there lived a  <div class="centered"> squirrel who lived  in </div>  the middle of the forest.  </div>

Fiddle Demo

Update: As pointed by @disule I have removed the deprecated center tag.

查看更多
叛逆
6楼-- · 2020-02-27 08:08

The woodcutter method is the <br/> tags

html:

<div class="center">
    <p>Once upon<br>
    a time, there lived a<br>  
    squirrel who lived in<br> 
    the middle of<br>
    the forest.</p>
</div>

css:

.center {
    width: auto;
    text-align: center;
}
查看更多
【Aperson】
7楼-- · 2020-02-27 08:09

Without word-break you can't be sure that the desired word is centered just as the line it's in.

Here is a different text, the first version the word 'who' centered, but the line not, the seccond the opposite:

 _________________________
|                         |
|        Once upon        |
|  a time, there lived a  |
|  squirrel who lived     |
|    somewhere else but   |
|      in the forest      |
|_________________________|
 _________________________
|                         |
|        Once upon        |
|  a time, there lived a  |
|    squirrel who lived   |
|    somewhere else but   |
|      in the forest      |
|_________________________|

As you can see without breaking the words it's entirely based on the text you have and the word you want to center if its possible or not.

Due this there's four solution:

  • Put only the desired word in that line and then try to be sure that theres exactly as much line before as after. (Only possible if the word is not the first or not the last or it's the only word in the text, and if the word is not roughly in the middle of the text you might end up with really arkward shapes.)

  • Count the position of the word inside the text and then break the words before and after to match the number of characters before and after the word in the same line.

  • Implement a word-breaking function in the language of the text, and calculate all the combinations of possible word- and line-breaks to find one (if any) where your desired word is centered.

  • Hardcode it by yourself, there are things that can be solved much more efficiently by a human than a simple program could (yet). After all we are well bundled supercomputers.

查看更多
登录 后发表回答