问题
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?
回答1:
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? :)
回答2:
[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(' ' + sPhrase + ' ');
$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:
Answer is, unfortunately, you can't do what you are asking with HTML/CSS.
To do this, you would need to either set some widths to force the text to break to the next line, or (preferably) insert a <BR>
tag where you want to break the line. HTML and/or CSS have no way of doing it for you. As Anderson notes below, you will be able to find a way to do it with JS if need be.
回答4:
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.
回答5:
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:
This is a really cool way to do this using javascript I made just for you! haha here is the fiddle http://jsfiddle.net/7Ykzp/2/
var words = text.split(" ");
we split it into words then try to fit them on a line we put this in a center in between two centers containing the previous and following text
a note we add 8 to the line width each time we try to add two more words to account for the size of spaces
edit: updated to properly place who based on word length
edit2: updated to now properly backwards wrap the text above the centered word! check it out!
回答7:
The easiest way is to just insert a line break <br>
after each line:
Here is an example jsfiddle
HTML:
<div class="container">
<p>
Once upon<br>
a time, there lived a<br>
squirrel who lived in<br>
the middle of<br>
the forest.
</p>
</div>
CSS:
.container {
border: 1px solid #666;
padding: 1em;
width: auto;
text-align: center
}
回答8:
The only answer to what you want to achieve is: It's not possible!
And I also doubt that it would make any sense. To center a word in an already centered line in a centered text - what should this be good for!?
Especially when it comes to fonts and font sizes there are so many unknown variables (used font, user stylesheets etc.), that every attempt based on these things will fail.
And even any JS solution will fail, as it is most likely that due to the word lengths it is not possible to exactly center the "word" in the line without changing/ adjusting the word spaces before and after the "word" to get it absolutely centered.
If you really want such a "dalliance" go and create an image on the fly. In an image you might achieve your goal. With HTML, CSS and JS you won't.
And if I think of such useful things like CSS hyphenation, it really makes no sense to me what you are trying to achieve. Sadly you didn't answer on my comment on your question what this all should be good for.
回答9:
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…
回答10:
<div>
<p class ="center"> Once upon </p>
<p class ="center">a time, there lived a</p><p class = "center">squirrel who
lived in</p><p class = "center"> the middle of </p>
<p class = "center">
the forest.
</p>
</div>
Try This with css code:
.center{
text-align:center;
letter-spacing:2px;
}
回答11:
While I agree that the html structure has issues, you mention that you have "little control" of it, so I am offering this answer purely with HTML as is. Without touching the HTML, the only way to get this with pure CSS is to allow the centered content to exist on its own line. So this works (see the fiddle):
center {
text-align: center;
}
center div {
width: 100%; /* really, the div should default to this, but just in case */
}
回答12:
Is your div a fixed width, or a variable width?
If it is a fixed width (the demo width you have given), then add the previous and following word to the div, and add
before and after.
Once upon a time, there lived a
squirrel who lived
in the middle of the forest..
and then center-align the paragraph.
.centered{ text-align: center }
If the div is a variable width, you could try a JavaScript function that accepts the div width and the font size as arguments, and that will decide how many words (both before and after) should be added.
It's a workaround that basically tries to fit as many words in a line as possible. Since a word is added after the "center" for every word added before the "center", your word should remain in the middle.
回答13:
Use CSS more specifically in the parent elements to target your words alignment. For example if there is a paragraph (p) and then after it their are two divs (div) then use the specific parent css property like this.
p div div {text-align:center; display:inline-block;}
that's it and it will do the work for you. Similarly you can target your given example with
center div {text-align:center; display:inline-block;}
回答14:
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;
}
回答15:
You can very simply do it in this way.
Fiddle Demo
Code:
<div style="position:absolute; left:50%; margin-left:-108px;">
<pre>
_________________________
| |
| Once upon a time, there |
| lived a squirrel who |
| lived in the middle of |
| the forest. |
|_________________________|
</pre>
</div>
回答16:
Do you have a CSS file linked? If you do, I would recommend changing the div tag to
<div class="who">who</div>
In the CSS file, you should have
.who {
text-align: center;
}
It is also possible that you force the html to include spaces with the following code:   (Include a semicolon at the end, it won't let me do it). So instead of having one space, you can have multiple non-breaking spaces.
来源:https://stackoverflow.com/questions/21506427/how-to-center-a-paragraph-of-text-on-a-particular-word-in-html-css-or-javascri