Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Поиск фразы по тексту (https://javascript.ru/forum/events/4313-poisk-frazy-po-tekstu.html)

Pattern 10.07.2009 15:25

Поиск фразы по тексту
 
Привет всем!
Кто может помочь с теорией (или примером), а то я что то уже запутался в край.
Итак, есть простой HTML-код:
<html>
<head>
<title>Test Search</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<script language="javascript">
//возвращает список textnode заданого node
function TNList(node,list){
	for(var a=0;a<node.childNodes.length;a++){
		if(node.childNodes[a].nodeType==3)
			list.push(node.childNodes[a]);
		else
			TNList(node.childNodes[a],list);
	}
}
//поиск и выделение объекта (DOM)
function search_and_select(b,str){
	var ltn=new Array();
	TNList(b,ltn);
	for(var a=0;a<ltn.length;a++){
		if(ltn[a].nodeValue.indexOf(str)!=-1){
			var s=window.getSelection();
			var r=document.createRange();
			r.setStart(ltn[a],ltn[a].nodeValue.indexOf(str));
			r.setEnd(ltn[a],ltn[a].nodeValue.indexOf(str)+str.length);
			s.removeAllRanges();
			s.addRange(r);
			alert('Found!');
		}else{
			alert('Not Found');
		}
	}
}
//инициализация
var init=function(){
	var str='text into';
	search_and_select(document.body,str);
};
</script>
</head>
<body onload="init();">
	<div id="child1">Any text into div <span id="child11">and text into span</span> tags</div>
</body>
</html>

По коду понятно, что при загрузке страницы будет произведён поиск фразы 'text into' по всем textnod'ам рекурсивно, находящихся в body. Всё бы хорошо, если поиск будет производиться только внутри textnod'ов. Но что, если, например, понадобится найти фразу 'div and text', у которой начало начинается в одной textnod'е, а окончание в другой? В теории всё понятно

setStart(document.getElementById('child1').firstChild,14);
setEnd(document.getElementById('child11').firstChild,8);

А вот как это практически реализовать что то никак сообразить не могу.
Есть у кого какие мысли по этому поводу?

x-yuri 14.07.2009 09:54

может уже поздно, но... можно получить innerText + внешний список DOM-элементов с указанием к какой его части они относятся

Pattern 14.07.2009 14:34

Нет, не поздно, вопрос ещё открыт и достаточно актуальный.
Однако innerText разве не присущ только для IE? Вопрос с IE не берётся во внимание вообще, потому что я под него написал класс, с помощью которого IE с Range работает по DOM-стандарту. К тому же, у IE есть свойство range.findText.
Вообще, мысли очень "хлипкие" по этому поводу.
Например, можно создать range всего документа и сузить его до границ body. Далее через selectNodeContents() и toString() вытащить его текстовое содержимое, где производить поиск фразы.
Фразу то он находит, а вот как получить граничные точки найденной фразы (startContainer, startOffset, endContainer, endOffset) пока не могу понять.

x-yuri 14.07.2009 14:55

ну я не имел в виду буквально innerText, я имел в виду получить текст содержащийся в элементе и всех его детях

т.е. можно создавать этот "innerText" и параллельно создавать другую структуру, в которой отмечать какой элемент в какой позиции "innerText" начинается и где заканчивается. Потому ищем в полученном "innerText" и по внешней структуре находим нужные элементы

Pattern 15.07.2009 11:15

Всё таки решил я эту задачу, но не полностью. Честно говоря, решение мне то же не очень нравится, очень похоже на "подгонку на ответ", но других мыслей пока не появилось.
В данном решении поиск фразы (например, "body Any text") производится по всему тексту и находит все, кроме 2 мест. В коде так же есть часть закоментированноого кода, которая, если её раскомментировать, находит все вхождения, но выделяет найденный текст со смещением. Как это побороть?
<html>
<head>
<title>Search Text</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<script language="javascript">
function getTextNodeList(parentNode,list){
	for(var a=0;a<parentNode.childNodes.length;a++){
		if(parentNode.childNodes[a].nodeType==3){
			if(parentNode.childNodes[a].nodeValue.length==1&&parentNode.childNodes[a].nodeValue==="\n")
				continue;
			else
				list.push({
					n: parentNode.childNodes[a],
					s: list[list.length-1]?parseInt(list[list.length-1].s)+parseInt(list[list.length-1].l):0,
					l: parentNode.childNodes[a].length
				});
		}else{
			getTextNodeList(parentNode.childNodes[a],list);
		}
	}
}

var storage={
	srch: '',
	lastIdx: -1,
	result: new Array(),
	clear: function(){
		with(storage){
			srch='';
			lastIdx=-1;
			result=new Array();
		}
	},
	search: function(){
		if(storage.srch!==''){
			var b=document.body;
			var list=new Array();
			getTextNodeList(b,list);
			var r=document.createRange();
			r.selectNodeContents(b);
			var c='';
			var v=null;
			for(var a=0;a<list.length;a++){
				v=list[a].n.nodeValue;
				c+=v.replace(/\n/gim," ");
/*
 * Если раскомментировать этот блок, ищет все фразы
 * но "со смещением"
				if(v.charAt(v.length-1)!==" "&&v.charAt(v.length-1)!=="\n"){
					console.log('"'+v+'"');
					c+=' ';
				}
 */
			}
			console.log(c);
			var p=new RegExp(storage.srch,"ig");
			var data=null;
			var startContainer=null;
			var startOffset=0;
			var endContainer=null;
			var endOffset=0;
			var start=0;
			var end=0;
			var ls=0;
			var ll=0;
			while((data=p.exec(c))!=null){
				start=p.lastIndex-storage.srch.length;
				end=p.lastIndex;
				for(var a=0;a<list.length;a++){
					ls=parseInt(list[a].s);
					ll=ls+parseInt(list[a].l);
					if(start>=ls&&start<ll){
						startContainer=list[a].n;
						startOffset=start-ls;
					}
					if(end>=ls&&end<ll){
						endContainer=list[a].n;
						endOffset=end-ls;
					}
				}
				storage.result.push({
					sc: startContainer,
					so: startOffset,
					ec: endContainer,
					eo: endOffset
				});
			}
		}
	},
	init: function(){
		storage.clear();
		if(arguments[0])
			with(storage){
				srch=arguments[0];
				search();
			}
	}
};
function findNext(){
	var i=document.getElementById('srchText');
	if(storage.result.length==0 || (storage.srch!==i.value && i.value!=='')){
		storage.init(i.value);
	}
	if(storage.result.length>0){
		var s=null;
		var r=null;
// for Gecko only
		if(window.getSelection){
			var s=window.getSelection();
			var r=document.createRange();
			storage.lastIdx++;
			if(storage.lastIdx>storage.result.length-1)
				storage.lastIdx=0;
			r.setStart(storage.result[storage.lastIdx].sc,storage.result[storage.lastIdx].so);
			r.setEnd(storage.result[storage.lastIdx].ec,storage.result[storage.lastIdx].eo);
			s.removeAllRanges();
			s.addRange(r);
		}
	}else{
		alert('Совпадений не найдено');
	}
}
</script>
</head>
<body>
Any text into body Any text into body
<div>Any text into body Any text into body <span>Any text into body Any text into body <span>Any text into body Any text into body</span></span></div>
<div>Any text into body Any text into body</div>
<span>Any text into body Any text into body</span>
Any text into body Any text into body
<br />
<input id="srchText" type="text" value="" /><input type="button" value="Search" onclick="findNext()" />
</body>
</html>


Часовой пояс GMT +3, время: 03:23.