我的 JavaScript 文件中有一个包含大量文本的字符串。我还有一个元素 div#container,它的样式(使用单独的 CSS)具有潜在的非标准 line-height
, font-size
, font-face
,也许还有其他人。它有固定的高度和宽度。
我想获得可以放入 div#container 的最大文本量,而不会从字符串中溢出。这样做的最佳方法是什么?
这需要能够处理用标签格式化的文本,例如:
<strong>Hello person that is this is long and may take more than a</strong>
line and so on.
// returns the part of the string that cannot fit into the object
$.fn.func = function(str) {
var height = this.height();
this.height("auto");
while(true) {
if(str == "") {
this.height(height);
return str; // the string is empty, we're done
}
var r = sfw(str); // r = [word, rest of String] (sfw is a split first word function defined elsewhere
var w = r[0], s = r[1];
var old_html = this.html();
this.html(old_html + " " + w);
if(this.height() > height)
{
this.html(old_html);
this.height(height);
return str; // overflow, return to last working version
}
str = s;
}
}
<ol>
<li>
<h2>Title</h2>
<ol>
<li>Character</li>
<ol>
<li>Line one that might go on a long time, SHOULD NOT BE BROKEN</li>
<li>Line two can be separated from line one, but not from itself</li>
</ol>
</ol>
<ol>
<li>This can be split from other</li>
<ol>
<li>Line one that might go on a long time, SHOULD NOT BE BROKEN</li>
<li>Line two can be separated from line one, but not from itself</li>
</ol>
</ol>
</li> <li>
<h2>Title</h2>
<ol>
<li>Character</li>
<ol>
<li>Line one that might go on a long time, SHOULD NOT BE BROKEN</li>
<li>Line two can be separated from line one, but not from itself</li>
</ol>
</ol>
<ol>
<li>This can be split from other</li>
<ol>
<li>Line one that might go on a long time, SHOULD NOT BE BROKEN</li>
<li>Line two can be separated from line one, but not from itself</li>
</ol>
</ol>
</li>
</ol>
最佳答案
好吧,让我尝试解决它;) 在实际思考解决方案时,我注意到我对您的需求了解得不够多,所以我决定开发简单的 JavaScript 代码并向您展示结果;尝试之后,您可以告诉我出了什么问题,以便我可以修复/更改它,交易?
我使用纯 JavaScript,没有 jQuery(如果需要,可以重写)。原理类似于你的 jQuery 插件:
sfw
函数那样的单词;它可以改变)container.innerHTML = "My String has a link <a href='#'";
在浏览器中我看到“My String has a link
”,所以“未完成”标签不会影响容器的大小(至少在我测试的所有浏览器中)<html>
<head>
<style>
div {
font-family: Arial;
font-size: 20px;
width: 200px;
height: 25px;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"> <strong><i>Strong text with <a href="#">link</a> </i> and </strong> simple text </div>
<script>
/**
* this function crops text inside div element, leaving DOMstructure valid (as much as possible ;).
* also it makes visible part as "big" as possible, meaning that last visible word will be split
* to show its first letters if possible
*
* @param container {HTMLDivElement} - container which can also have html elements inside
* @return {String} - visible part of html inside div element given
*/
function cropInnerText( container ) {
var fullText = container.innerHTML; // initial html text inside container
var realHeight = container.clientHeight; // remember initial height of the container
container.style.height = "auto"; // change height to "auto", now div "fits" its content
var i = 0;
var croppedText = "";
while(true) {
// if initial container content is the same that cropped one then there is nothing left to do
if(croppedText == fullText) {
container.style.height = realHeight + "px";
return croppedText;
}
// actually append fullText characters one by one...
var nextChar = fullText.charAt( i );
container.innerHTML = croppedText + nextChar;
// ... and check current height, if we still fit size needed
// if we don't, then we found that visible part of string
if ( container.clientHeight > realHeight ) {
// take all opening tags in cropped text
var openingTags = croppedText.match( /<[^<>\/]+>/g );
if ( openingTags != null ) {
// take all closing tags in cropped text
var closingTags = croppedText.match( /<\/[^<>]+>/g ) || [];
// for each opening tags, which are not closed, in right order...
for ( var j = openingTags.length - closingTags.length - 1; j > -1; j-- ) {
var openingTag;
if ( openingTags[j].indexOf(' ') > -1 ) {
// if there are attributes, then we take only tag name
openingTag = openingTags[j].substr(1, openingTags[j].indexOf(' ')-1 ) + '>';
}
else {
openingTag = openingTags[j].substr(1);
}
// ... close opening tag to have valid html
croppedText += '</' + openingTag;
}
}
// return height of container back ...
container.style.height = realHeight + "px";
// ... as well as its visible content
container.innerHTML = croppedText;
return croppedText;
}
i++;
croppedText += nextChar;
}
}
var container = document.getElementById("container");
var str = cropInnerText( container );
console.info( str ); // in this case it prints '<strong><i>Strong text with <a href="#">link</a></i></strong>'
</script>
</body>
fullText
如果需要(我也不会更改),请放回容器中 sfw
功能,因此您可以轻松更改它。 "our sentence"
,可能是visible只是第一个( "our"
),并且“句子”应该被剪掉( overflow:hidden
会这样工作)。就我而言,我将逐个字符地附加,所以我的结果可以是 "our sent"
.同样,这不是算法的复杂部分,因此根据您的 jQuery 插件代码,您可以更改我的以使用单词。 <li>, <h1>
的问题...例如我有包含内容的容器:<div id="container"> <strong><i>Strong text with <ul><li>link</li></ul> </i> and </strong> simple text </div>
...
"<strong><i>Strong text with <" -> "<strong><i>Strong text with <"
"<strong><i>Strong text with <u" -> "<strong><i>Strong text with "
"<strong><i>Strong text with <ul" -> "<strong><i>Strong text with <ul></ul>" // well I mean it recognizes ul tag and changes size of container
"<strong><i>Strong text with <u</i></strong>"
- 与 "<u"
,有什么不好。在这种情况下我需要处理的是,如果我们找到我们的结果字符串(根据算法 "<strong><i>Strong text with <u"
),我们需要删除最后一个“未关闭”标签(在我们的例子中是 "<u"
),所以在关闭标签之前有效的 html 我添加了以下内容:...
if ( container.clientHeight > realHeight ) {
/* start of changes */
var unclosedTags = croppedText.match(/<[\w]*/g);
var lastUnclosedTag = unclosedTags[ unclosedTags.length - 1 ];
if ( croppedText.lastIndexOf( lastUnclosedTag ) + lastUnclosedTag.length == croppedText.length ) {
croppedText = croppedText.substr(0, croppedText.length - lastUnclosedTag.length );
}
/* end of changes */
// take all opening tags in cropped text
...
>
取走所有标签(在我们的例子中,它返回 ["<strong", "<i", "<u"]
); "<u"
) croppedText
字符串,然后我们将其删除 "<strong><i>Strong text with </i></strong>"
While this library doesn't cover the full gamut of possible weirdness that HTML provides, it does handle a lot of the most obvious stuff.
<script src="http://ejohn.org/files/htmlparser.js"></script>
<script>
function cropInnerText( container ) {
var fullText = container.innerHTML;
var realHeight = container.clientHeight;
container.style.height = "auto";
var i = 0;
var croppedText = "";
while(true) {
if(croppedText == fullText) {
container.style.height = realHeight + "px";
return croppedText;
}
var nextChar = fullText.charAt( i );
container.innerHTML = croppedText + nextChar;
if ( container.clientHeight > realHeight ) {
// we still have to remove unended tag (like "<u" - with no closed bracket)
var unclosedTags = croppedText.match(/<[\w]*/g);
var lastUnclosedTag = unclosedTags[ unclosedTags.length - 1 ];
if ( croppedText.lastIndexOf( lastUnclosedTag ) + lastUnclosedTag.length == croppedText.length ) {
croppedText = croppedText.substr(0, croppedText.length - lastUnclosedTag.length );
}
// this part is now quite simple ;)
croppedText = HTMLtoXML(croppedText);
container.style.height = realHeight + "px";
container.innerHTML = croppedText ;
return croppedText;
}
i++;
croppedText += nextChar;
}
}
</script>
https://stackoverflow.com/questions/4917244/