首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >JavaScript -高效地查找包含大量字符串中的一个的所有元素。

JavaScript -高效地查找包含大量字符串中的一个的所有元素。
EN

Stack Overflow用户
提问于 2010-04-23 16:08:47
回答 2查看 393关注 0票数 0

我有一组字符串,我需要查找HTML文档中的所有事件。字符串发生的位置很重要,因为我需要以不同的方式处理每一种情况:

  • 字符串是属性的全部或部分。例如,字符串是foo: element.
  • String的<input value="foo"> ->添加类ATTR是元素的全文。例如,element.
  • String中的<button>foo</button> ->添加类文本在元素的文本中是内联的。例如,<p>I love foo</p> ->用类文本将文本封装在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>替换它们,尽管我不确定这是否是最有效的方法。

对于这些属性,我可以这样做:

代码语言:javascript
复制
sortedStrings.each(function(it) {
     document.body.innerHTML.replace(new RegExp('(\S+?)="[^"]*'+escapeRegExChars(it)+'[^"]*"','g'),function(s,attr) {
        $('[+attr+'*='+it+']').addClass('ATTR');
     });
});

再一次,这似乎没有效率。

最后,对于全文元素,对比较innerHTML和每个字符串的文档进行深度优先搜索是可行的,但是对于大量字符串来说,它似乎效率很低。

任何提供性能改进的答案都会获得更高的选票:)

编辑:我修改了鲍勃的答案。delim是字符串周围的一个可选分隔符(用于区分它与普通文本),而keys是字符串列表。

代码语言:javascript
复制
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));
            }
        });
    });
});

假设遍历是最昂贵的操作,这似乎是最优的,尽管改进仍然是受欢迎的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-04-23 17:42:33

尝试用regex解析HTML是个游戏。它甚至不能处理HTML的基本结构,更别提这些怪癖了。你的片段已经有很多问题了。(不检测未引用的属性;由于缺少it转义、regex转义或CSS-转义(*);-中的属性失败;奇怪地不使用replace.)

所以,用DOM。是的,那就意味着穿越。但是,像您已经使用的[attr*=]这样的选择器也是如此。

代码语言:javascript
复制
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编码,但也不是那样的。(这几乎总是错误的;避免。)

票数 2
EN

Stack Overflow用户

发布于 2010-04-23 16:18:14

没有什么好办法来做这件事。您的最后一个需求使您必须遍历整个区域。

对于前两个需求,我将按标记名称选择所有元素,并在它们之上插入所需的内容。

只有我能想到的性能改进是不惜一切代价在服务器端做这件事,这甚至可能意味着额外的帖子让你的更快的服务器来完成这项工作,否则这可能会非常慢,比如说,IE6。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2700120

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档