我的JavaScript文件中有一个包含大量文本 text 的字符串。我还有一个元素, div#container ,它使用可能不标准的line-height
,font-size
,font-face
以及其他可能非标准<strong>Hello person that is this is long and may take more than a</strong>
line and so on.
进行样式设置(使用单独的CSS)。它有一个固定的高度和宽度。
我想获得可容纳 div#container 的最大文本量,而不会从字符串中溢出。这样做的最佳方式是什么?
这需要能够处理使用标签格式化的文本,例如:
// 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;
}
}
目前,我有一个适用于纯文本的JQuery插件,代码如下:
<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>
更新:
数据如下所示:
{{1}}
答案 0 :(得分:5)
获得最长的第一行:
visibility:hidden;
创建一个DIV(因此它将具有维度),但将其定位为position:absolute;
,以便它不会破坏您的页面流量DIV
DIV
相同,但保留width:auto;
DIV
的宽度。结果是您可以放入的文字。
如果您需要查找适合容器的行数,请调整算法以保留height:auto;
并设置固定width
。
自动调整textareas使用相同的技术,当用户输入文本时,textareas会自动增长。
答案 1 :(得分:5)
好吧,让我试着解决它;)实际上考虑解决方案我注意到我对你的要求知之甚少,所以我决定开发简单的JavaScript代码并向你展示结果;在尝试之后你可以告诉我什么是错的,所以我可以修理/更改它,交易?
我使用纯JavaScript,没有jQuery(如果需要可以重写)。原理类似于你的jQuery插件:
sfw
函数那样的字;它可以更改)container.innerHTML = "My String has a link <a href='#'";
时,我看到“My String has a link
”,因此“未完成”标签不会影响容器的大小(至少在我测试过的所有浏览器中)测试它的HTML页面:
<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"
,则可见只有第一个("our"
),并且应该删除“句子”(overflow:hidden
将以这种方式工作) 。在我的情况下,我会按字符追加字符,因此我的结果可以是"our sent"
。同样,这不是算法的复杂部分,因此根据您的jQuery插件代码,您可以更改我的单词。问题,评论,发现的错误是受欢迎的;)我在IE9,FF3.6,Chrome 9中进行了测试
更新:正在解决<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>"
UPDATE2 谢谢你的例子,所以我看到你没有嵌套标签,但它们也有“树”结构,实际上我没有考虑到它,但它仍然可以修复;)一开始我想编写适当的“解析器”,但是当我不工作的时候我总是得到一个例子,所以我认为最好找到已编写的解析器,并且有一个: Pure JavaScript HTML Parser。它还有一个 shag :
虽然这个库没有覆盖 完全可能的奇怪之处 HTML提供,它确实处理了很多 最明显的东西。
但是你的例子可行;该库未考虑开放标记的位置,但
我认为有了这个假设,这个库很好用。然后结果函数看起来像:
<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>
答案 2 :(得分:1)
要解决此问题,您需要其他信息:
对于'切到哪里'的问题,你可能需要在输入字符串的关键点注入唯一的<a name="uniq"/>
锚标记(比如......在输入中的每个开始标记之前?)。然后,您可以测试每个锚点的布局位置,并找到打破输入的位置。
找到最合理的断点,你需要在前半部分的末尾添加标签以关闭它,并在下半部分的前面添加标签以打开它。因此,当您解析输入字符串以便先找到开始标记时,您在注入<a/>
时会保留一个“标记堆栈”列表。查找与此paritcular相关的标记堆栈,然后根据需要添加标记。
我可以发现2个陷阱:
<a/>
处打破最终,在我看来,你正在等待HTML5的列构造。
答案 3 :(得分:0)
您是否只想在段落中设置第一行的格式? CSS :first-line伪选择器是否适用于您的应用程序?
答案 4 :(得分:0)
您可以使用getComputedStyle找出内联元素的宽度(即它具有display: inline
属性):
window.getComputedStyle(element, null).getPropertyValue("width");
如果事实证明此元素的宽度大于其容器的宽度,则会逐步删除一些文本,直到它适合。