天天看點

javascript實作文本高亮

  一個實驗項目需要用到文本高亮的功能,使用javascript實作文本高亮的方法,暫時能想到的方法就是在給文本加上标簽,然後添加樣式。而如何給文本包裹一個标簽,暫時也隻想到兩個方法:1、使用dom操作 2、用正則修改html代碼

下午試驗了一下使用dom操作的方法

/**
 * 使用原生javascript操作dom,實作文本高亮
 */

var HIGH_LIGHT_CLS = 'high-light';
var WRAP_TAG_NAME = 'a';
var TEXT_TYPE = 3;
var ELEMENT_TYPE = 1;
var COLOR = '#FFFF96';

Highlighter = function() {
	this.lastKey = null;
};

Highlighter.prototype = {
	/**
	 * 取消高亮
	 * @param {ElementNode} node #require
	 * @param {Node} parent #imply
	 */
	lowLightNode : function(node, parent) {
		//如果不是元素節點則退出
		if (!node || node.nodeType != ELEMENT_TYPE) {
			return;
		}
		//如果parent參數沒傳則預設使用node節點的父親節點
		parent = parent ? parent : node.parentNode;
		//取得節點對應的文本值
		var text = node.firstChild.nodeValue + '';
		//使用文本節點替換元素節點
		parent.replaceChild(document.createTextNode(text), node);
	},

	/**
	 * 高亮
	 * @param {String} key 關鍵字 #require
	 * @param {TextNode} node #require
	 * @param {Node} parent #imply
	 */
	highlightNode : function(key, node, parent) {
		//如果不是文本節點則退出
		if (!node || node.nodeType != TEXT_TYPE) {
			return;
		}
		//如果沒有傳parent參數則parent預設為node節點的父親節點
		parent = parent ? parent : node.parentNode;
		//大小小不敏感
		key = key.toLowerCase();
		//取得文本節點的值
		var text = node.nodeValue;
		//每次截取關鍵字後剩餘的文本
		var remain = text + '';
		//每次截取時關鍵字對于文本值的的索引
		var index;
		//建立片段
		var fragment = document.createDocumentFragment();
		var count = 0;
		
		//對文本内容循環截取關鍵字,截取的每部分都包裹上一個classname為'high-light'
		//背景色為黃色, 的 a标簽,
		while ((index = remain.toLowerCase().indexOf(key)) != -1) {
			
			//關鍵字前面部分的文本
			var beforeMatch = remain.substring(0, index);
			if (beforeMatch.length > 0) {
				fragment.appendChild(document.createTextNode(beforeMatch));
				count++;
			}
			
			//對文本包裹了a标簽後的元素
			var wrapEl = document.createElement(WRAP_TAG_NAME);
			//設定包裹元素的html代碼
			wrapEl.innerHTML = remain.substring(index, index + key.length);
			//設定classname
			wrapEl.setAttribute("class", HIGH_LIGHT_CLS);
			wrapEl.setAttribute("className", HIGH_LIGHT_CLS); // for IE
			//設定背景色
			wrapEl.style.backgroundColor = COLOR;
			//往片段添加包裹後的文本相關節點
			fragment.appendChild(wrapEl);
			count++;

			remain = remain.substring(index + key.length);
		}
		//截取完關鍵字後,添加剩餘的文本
		if (remain.length > 0) {
			fragment.appendChild(document.createTextNode(remain));
			count++;
		}
		//将截取到的對應的文本節點替換成a标簽元素節點
		parent.replaceChild(fragment, node);
		//傳回截取到的關鍵字數量
		return count;
	},

	//判斷一個節點(節點是否為對應關鍵字的包裹節點)
	isWrapEl : function(node, key) {
		if (key) {
			//比較節點類型
			if (node.nodeType == ELEMENT_TYPE) {
				//比較标簽名
				if (node.tagName.toLowerCase() == WRAP_TAG_NAME.toLowerCase()) {
					//比較classname
					var cls = (node.getAttribute("class") || node.getAttribute("className") || "").toLowerCase();
					if (cls.indexOf(HIGH_LIGHT_CLS.toLowerCase()) != -1) {
						var firstNode = node.firstChild;
						//比較文本值
						if (firstNode
								&& firstNode.nodeValue
								&& firstNode.nodeValue.toLowerCase().indexOf(
										key.toLowerCase()) != -1) {
							return true;
						}
					}
				}
			}
		}
		return false;
	},
	
	/**
	 * 
	 * @param {HtmlElement} el #required 對el元素的所有子節點進行關鍵字高亮
	 * @param {String} key #required 關鍵字
	 * @param {bool} earseLast #imply 是否擦出上一次高亮(可能兩次高亮的關鍵字不一樣)
	 * @param {bool} forece #imply 強制高亮
	 */
	highlight : function(el, key, earseLast, force) {
		if(!el) return;
		if (this.lastKey == key && force !== true) return;
		doHighlight(el, key, earseLast, this);
		this.lastKey = key;
	},
	
	/**
	 * @param {} el #required 對el元素的所有子節點取消所有文本高亮
	 */
	removeAllHighlight : function(el){
		if(!el) return;
		doRemoveAllHighlight(el, this);
		this.lastKey = null;
	}
};

//隻是不想上邊代碼太長,就寫下來了
function doRemoveAllHighlight(el, me){
	//擷取el下的所有子節點
	var nodes = el.childNodes;
	for ( var i = 0; i < nodes.length; i++) {
		var node = nodes[i];
		var nodeType = node.nodeType;
		//如果是元素節點
		if (nodeType == ELEMENT_TYPE) {
			//如果确定是高亮包裹的元素
			if (me.isWrapEl(node, me.lastKey)) {
				//進行取消高亮操作
				me.lowLightNode(node);
			}else{
				//遞歸子節點進行取消高亮				
				doRemoveAllHighlight(node, me);
			}
		}
	}
} 

function doHighlight (el, key, earseLast, me) {
	//大小寫不敏感
	key = key.toLowerCase();
	//獲得el下的子節點
	var nodes = el.childNodes;
	for ( var i = 0; i < nodes.length; i++) {
		var node = nodes[i];
		var nodeType = node.nodeType;
		// 如果是文本節點
		if (nodeType == TEXT_TYPE) {
			var text = node.nodeValue;
			// 如果該文本節點的文本值包含關鍵字
			if (text.toLowerCase().indexOf(key) != -1) {
				//進行高亮 (因為nodes.length會實時對應dom的狀态做更新,是以要改變疊代的i)
				i = i + me.highlightNode(key, node, el) - 1;
			}
		}
		// 如果是元素節點
		else if (nodeType == ELEMENT_TYPE) {
			//判斷是否為上次高亮包裹的元素,并且是否需要擦出
			if (me.isWrapEl(node, me.lastKey) && earseLast === true) {
				//擦出上次高亮
				me.lowLightNode(node);
				// 目前索引對應的節點再走一次
				i--;
			} else {
				//遞歸進行高亮
				doHighlight(node, key, earseLast, me);
			}
		}
	}
}
      

繼續閱讀