Bu zor olacak.
Eğer bir etiketi içinde bir şey, naif gibi bir şey yok sayarak, basit regex hack ile yapabilirdi ederken:
preg_replace(
'My(<[^>]>)*\s+(<[^>]>)*name(<[^>]>)*\s+(<[^>]>)*is(<[^>]>)*\s+(<[^>]>)*Josh',
'<span class="marked">$0</span>', $html
)
bu hiç de güvenilir değil. Kısmen HTML regex çözümlenen olamaz: bir öznitelik değerinde > koymak geçerli, ve yorum gibi diğer non-element yapıları yanlış ayrıştırılır olacak. <[^>\s]*(\s+([^>\s]+(\s*=\s*([^"'\s>][\s>]*|"[^"]*"|'[^']*')\s*))?)*\s*\/?>, hala giriş HTML geçerli garanti değil, özellikle eğer, aynı sorunların birçok olurdu gibi korkunç hantal bir şey - hatta etiketleri maç için daha titiz bir ifade ile.
Bu bile işliyoruz HTML güvenilmeyen, sanki o betik enjeksiyonu sonucunda, nitelikleri içine metin içeriği dönüm içine Ayrıştırıcı aptal olabilir, bir security issue olabilir.
Ama bu bile görmezden, uygun eleman yuvalama sağlamak mümkün olmaz. Yani açmak olabilir:
<em>My name is <strong>Josh</strong>!!!</em>
içine misnested ve geçersiz:
<span class="marked"><em>My name is <strong>Josh</strong></span>!!!</em>
veya:
My
<table><tr><td>name is</td></tr></table>
Josh
Bu elemanlar bir yayılma ile sarılmış olamaz nerede. Eğer şanssız iseniz, tarayıcı fixups 'doğru' daki geçersiz çıktı 'işaretlenmiş' yarım sayfa bırakarak, ya da sayfa düzeni karışıklık bitebileceğini için.
Yani bir çözümlü-DOM düzeyde yerine dize hack ile bunu yapmak zorunda olacaktır. PHP, süreç o ve yeniden tefrika kullanarak tüm dizeyi ayrıştırmak olabilir, ama o bakış bir erişilebilirlik açısından kabul edilebilir ise, muhtemelen içerik zaten ayrıştırılır JavaScript içinde tarayıcı sonunda bunu yapmak daha kolay olacaktır DOM düğümleri.
Hala oldukça zor olacak. This question metin tüm aynı metin düğümü içinde olacaktır nerede kolları, ama bu çok daha basit bir durum.
Ne etkili yapmak gerekir olacaktır:
for each Element that may contain a <span>:
for each child node in the element:
generate the text content of this node and all following siblings
match the target string/regex against the whole text
if there is no match:
break the outer loop - on to the next element.
if the current node is an element node and the index of the match is not 0:
break the inner loop - on to the next sibling node
if the current node is a text node and the index of the match is > the length of the Text node data:
break the inner loop - on to the next sibling node
// now we have to find the position of the end of the match
n is the length of the match string
iterate through the remaining text node data and sibling text content:
compare the length of the text content with n
less?:
subtract length from n and continue
same?:
we've got a match on a node boundary
split the first text node if necessary
insert a new span into the document
move all the nodes from the first text node to this boundary inside the span
break to outer loop, next element
greater?:
we've got a match ending inside the node.
is the node a text node?:
then we can split the text node
also split the first text node if necessary
insert a new span into the document
move all contained nodes inside the span
break to outer loop, next element
no, an element?:
oh dear! We can't insert a span here
Ahh.
Burada ayrı bir maçın parçası olan her bir metin düğümü sarmak için kabul edilebilir ise, biraz daha az kötü olan alternatif bir öneri bulunuyor. Yani:
<p>Oh, my</p> name <div><div>is</div><div> Josh
çıkışı ile bırakacaktı:
<p>Oh, <span class="marked">my</span></p>
<span class="marked"> name </span>
<div><div><span class="marked">is</span></div></div>
<span class="marked"> Josh</span>
hangi maçları şekillendirme konum nasıl bağlı, OK görünebilir. Ayrıca kısmen öğeleri içinde kibrit misnesting sorunu çözmek olacaktır.
ETA: Oh pseudocode kahretsin, ben daha fazla veya daha az zaten şimdi kod yazdık, hem de bunu bitirmek olabilir. İşte ikinci yaklaşımın bir JavaScript versiyonu:
markTextInElement(document.body, /My\s+name\s+is\s+Josh/gi);
function markTextInElement(element, regexp) {
var nodes= [];
collectTextNodes(nodes, element);
var datas= nodes.map(function(node) { return node.data; });
var text= datas.join('');
// Get list of [startnodei, startindex, endnodei, endindex] matches
//
var matches= [], match;
while (match= regexp.exec(text)) {
var p0= getPositionInStrings(datas, match.index, false);
var p1= getPositionInStrings(datas, match.index+match[0].length, true);
matches.push([p0[0], p0[1], p1[0], p1[1]]);
}
// Get list of nodes for each match, splitted at the edges of the
// text. Reverse-iterate to avoid the splitting changing nodes we
// have yet to process.
//
for (var i= matches.length; i-->0;) {
var ni0= matches[i][0], ix0= matches[i][1], ni1= matches[i][2], ix1= matches[i][3];
var mnodes= nodes.slice(ni0, ni1+1);
if (ix1<nodes[ni1].length)
nodes[ni1].splitText(ix1);
if (ix0>0)
mnodes[0]= nodes[ni0].splitText(ix0);
// Replace each text node in the sublist with a wrapped version
//
mnodes.forEach(function(node) {
var span= document.createElement('span');
span.className= 'marked';
node.parentNode.replaceChild(span, node);
span.appendChild(node);
});
}
}
function collectTextNodes(texts, element) {
var textok= [
'applet', 'col', 'colgroup', 'dl', 'iframe', 'map', 'object', 'ol',
'optgroup', 'option', 'script', 'select', 'style', 'table',
'tbody', 'textarea', 'tfoot', 'thead', 'tr', 'ul'
].indexOf(element.tagName.toLowerCase()===-1)
for (var i= 0; i<element.childNodes.length; i++) {
var child= element.childNodes[i];
if (child.nodeType===3 && textok)
texts.push(child);
if (child.nodeType===1)
collectTextNodes(texts, child);
};
}
function getPositionInStrings(strs, index, toend) {
var ix= 0;
for (var i= 0; i<strs.length; i++) {
var n= index-ix, l= strs[i].length;
if (toend? l>=n : l>n)
return [i, n];
ix+= l;
}
return [i, 0];
}
// We've used a few ECMAScript Fifth Edition Array features.
// Make them work in browsers that don't support them natively.
//
if (!('indexOf' in Array.prototype)) {
Array.prototype.indexOf= function(find, i /*opt*/) {
if (i===undefined) i= 0;
if (i<0) i+= this.length;
if (i<0) i= 0;
for (var n= this.length; i<n; i++)
if (i in this && this[i]===find)
return i;
return -1;
};
}
if (!('forEach' in Array.prototype)) {
Array.prototype.forEach= function(action, that /*opt*/) {
for (var i= 0, n= this.length; i<n; i++)
if (i in this)
action.call(that, this[i], i, this);
};
}
if (!('map' in Array.prototype)) {
Array.prototype.map= function(mapper, that /*opt*/) {
var other= new Array(this.length);
for (var i= 0, n= this.length; i<n; i++)
if (i in this)
other[i]= mapper.call(that, this[i], i, this);
return other;
};
}