为元素文字高亮关键字

已知一个元素: <div>123abc&amp;de</div> ,我们想高亮文字"a",也就是说,把元素内容变为

<div>123<span class='highlight'>a</span>bc&amp;de</div>。要如何做?


这还不简单,把div的innerHTML做个replace,把 a替换成 <span class='highlight'>a</span> 不就成了?注意,字符实体&amp;里面也有个a,replace会破坏它的,如果用正则控制不替换&和;之间的字符似乎又比较麻烦。我这里有个方法:


1. 把 div 的 innerText 用关键字拆分开,得到["123", "bc&de"]

2. 把这个数组中的字符串转换成HTML格式,即得到["123", "bc&amp;de"]

3. 用<span class='highlight'>a</span> 这个字符串join上面那个数组,即得到 123<spanclass='highlight'>a</span>bc&amp;de

这样我们就得到了div高亮后的innerText。


不过别高兴太早,需求总是会变化的:如果要高亮3a和abc这两个关键字怎么办?上面那种做法完全没用了。今天想到一个比较笨的办法,构造一个数组markArray,数组长度和div的innerText的长度一样,记录innerText中的每一个字是否被高亮,这个用indexOf可以做到。然后把单个的字进行合并,得到若干被高亮和不被高亮的字符串,最后把这些字符串数组进行拼接。仍以上述为例:


1. 3a和abc高亮,markArray为[0, 0, 1, 1, 1, 1, 0, 0, 0]

2. 合并后,得到3个字符串,12不高亮,3abc高亮,&de不高亮

3. 这3个字符串转换为HTML格式,高亮的串用<span>包裹,最后得到12<span style='highlight'>3abc</span>&amp;de


代码

var highlightUtil = {
    _sourceText: "",
    _keys: [],
    _blocks: [],
    _matchMarkArray: [],
    
    highlight: function(elt, keys) {
        this._sourceText = elt.innerText;
        this._keys = keys;
        
        this._mark();
        this._combine();
        var innerHTML = this._processBlocks();
        
        elt.innerHTML = innerHTML;
    },
    
    _getMatchPos: function(matchStr) {
        var posArray = [];
        var start = 0;
        
        while (true) {
            var index = this._sourceText.indexOf(matchStr, start);
            if (index == -1) {
                break;
            } else {
                posArray.push(index);
                start = index + matchStr.length;
            }
        }
        
        return posArray;
    },
   
    _mark: function() {
        this._matchMarkArray = [];
        for (var i = 0; i < this._sourceText.length; i++) {
            this._matchMarkArray.push(false);
        }
        
        for (var i = 0; i < this._keys.length; i++) {
            var key = this._keys[i];
            var posArray = this._getMatchPos(key);
            for (var p = 0; p < posArray.length; p++) {
                for (var c = 0; c < key.length; c++) {
                    this._matchMarkArray[posArray[p] + c] = true;
                }
            }
        }
    },
    
    _combine: function() {
        if (this._matchMarkArray.length == 0) {
            return;
        }
        
        this._blocks = [];
        
        var start = 0;
        var len = 0;
        var lastMark = this._matchMarkArray[0];
        
        for (var i = 0; i < this._matchMarkArray.length; i++) {
            var mark = this._matchMarkArray[i];
            if (mark == lastMark) {
                len++;
            } else {
                var block = [start, len, lastMark];
                this._blocks.push(block);
                
                start = i;
                len = 1;
                lastMark = mark;
            }
        }
        
        var lastBlock = [start, len, lastMark];
        this._blocks.push(lastBlock);
    },
    
    _processBlocks: function() {
        var htmlStr = "";
        for (var i = 0; i < this._blocks.length; i++) {
            var block = this._blocks[i];
            var start = block[0];
            var end = block[0] + block[1];
            var highlight = block[2];
            var sourceText = new String(this._sourceText);
            
            var textBlock = sourceText.slice(start, end);
            console.log(textBlock);
            
            if (highlight) {
                htmlStr += "<span class='highlight'>" + this._textToHtml(textBlock) + "</span>";
            } else {
                htmlStr += this._textToHtml(textBlock);
            }
        }
        
        return htmlStr;
    },

    _textToHtml: function(text) {
        var tmpElt = document.createElement("div");
        var tmpText = document.createTextNode(text);
        tmpElt.appendChild(tmpText);
        return tmpElt.innerHTML;
    }
};

使用 highlightUtil.highlight(div, ["3a", "abc"]) 高亮字符串。这个算法比较低效,可以看到_mark方法用了三层循环,文本内容较短的元素还行,而且元素里不能再嵌套元素。以后再尝试改进算法。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值