我有一组字符串,我需要在HTML文档中找到所有出现的内容。字符串出现的位置很重要,因为我需要以不同的方式处理每个案例:
字符串是属性的全部或部分。例如,字符串是foo:<input value="foo">
- &gt;将类ATTR添加到元素。
String是元素的全文。例如,<button>foo</button>
- &gt;将类TEXT添加到元素。
字符串在元素的文本中是内联的。例如,<p>I love foo</p>
- &gt;使用TEXT类在span标记中包装文本。
另外,我需要先匹配最长的字符串。例如,如果我有foo和foobar,那么<p>I love foobar</p>
应该变为<p>I love <span class="TEXT">foobar</span></p>
,而不是<p>I love <span class="TEXT">foo</span>bar</p>
。
内联文字很简单:按字母长度降序排序,找到并用document.body.innerHTML
替换<span class="TEXT">$1</span>
中的每一个,但我不确定这是否是最有效的方法。
对于属性,我可以这样做:
sortedStrings.each(function(it) {
document.body.innerHTML.replace(new RegExp('(\S+?)="[^"]*'+escapeRegExChars(it)+'[^"]*"','g'),function(s,attr) {
$('[+attr+'*='+it+']').addClass('ATTR');
});
});
再次,这似乎效率低下。
最后,对于全文元素,文档的深度优先搜索将innerHTML
与每个字符串进行比较将起作用,但对于大量字符串,它似乎非常低效。
任何提供性能改进的答案都会得到支持:)
编辑:我对Bob的回答进行了修改。delim
是字符串周围的可选分隔符(以区别于普通文本),keys
是字符串列表。
function dfs(iterator,scope) {
scope = scope || document.body;
$(scope).children().each(function() {
return dfs(iterator,this);
});
return iterator.call(scope);
}
var escapeChars = /['\/.*+?|()[\]{}\\]/g;
function safe(text) {
return text.replace(escapeChars, '\\$1');
}
function eachKey(iterator) {
var key, lit, i, len, exp;
for(i = 0, len = keys.length; i < len; i++) {
key = keys[i].trim();
lit = (delim + key + delim);
exp = new RegExp(delim + '(' + safe(key) + ')' + delim,'g');
iterator(key,lit,exp);
}
}
$(function() {
keys = keys.sort(function(a,b) {
return b.length - a.length;
});
dfs(function() {
var a, attr, html, val, el = $(this);
eachKey(function(key,lit,exp) {
// check attributes
for(a in el[0].attributes) {
attr = el[0].attributes[a].nodeName;
val = el.attr(attr);
if(exp.test(val)) {
el.addClass(attrClass);
el.attr(attr,val.replace(exp,"$1"));
}
}
// check all content
html = el.html().trim();
if(html === lit) {
el.addClass(theClass);
el.html(key); // remove delims
} else if(exp.test(html)) {
// check partial content
el.html(html.replace(exp,wrapper));
}
});
});
});
假设遍历是最昂贵的操作,这似乎是最佳的,尽管仍然可以进行改进。
答案 0 :(得分:2)
尝试用正则表达式解析HTML是一个杯子的游戏。它根本无法处理HTML的基本结构,更不用说怪癖了。您的代码段已经存在很多错误。 (不检测未加引号的属性;由于缺少HTML转义,正则表达式转义或CSS转义(*)而导致it
中的各种标点符号失败;对于-
的属性失败;奇怪的不使用replace
...)
所以,使用DOM。是的,这意味着一次遍历。但是那样的选择器就像你已经使用的[attr*=]
一样。
var needle= 'foo';
$('*').each(function() {
var tag= this.tagName.toLowerCase();
if (tag==='script' || tag==='style' || tag==='textarea' || tag==='option') return;
// Find text in attribute values
//
for (var attri= this.attributes.length; attri-->0;)
if (this.attributes[attri].value.indexOf(needle)!==-1)
$(this).addClass('ATTR');
// Find text in child text nodes
//
for (var childi= this.childNodes.length; childi-->0;) {
var child= this.childNodes[childi];
if (child.nodeType!=3) continue;
// Sole text content of parent: add class directly to parent
//
if (child.data==needle && element.childNodes.length===1) {
$(this).addClass('TEXT');
break;
}
// Else find index of each occurence in text, and wrap each in span
//
var parts= child.data.split(needle);
for (var parti= parts.length; parti-->1;) {
var span= document.createElement('span');
span.className= 'TEXT';
var ix= child.data.length-parts[parti].length;
var trail= child.splitText(ix);
span.appendChild(child.splitText(ix-needle.length));
this.insertBefore(span, trail);
}
}
});
(反向循环是必要的,因为这是内容的破坏性迭代。)
(*:escape
没有做任何这些事情。它更像是URL编码,但它也不是真的。它几乎总是错误的;避免。)
答案 1 :(得分:1)
真的没有办法做到这一点。你的最后一项要求使你必须遍历整个dom。
对于前2个要求,我将按标签名称选择所有元素,并根据需要对它们进行交互。
只有我能想到的性能改进就是不惜一切代价在服务器端执行此操作,这甚至可能意味着让更快的服务器完成工作的额外帖子,否则这可能会非常慢,比如,IE6