Scott B
Scott B

Reputation: 40167

DOMDocument foreach replacement

In the foreach loop below, what is the proper syntax in order to return only the first instance of the keyword, wrapping it in bold tags, then exiting the loop and function?

For example, the keyword is "blue widgets". So I want the first appearance of the string (in $content) to be changed from blue widgets to

<b>blue widgets</b>

Here's the routine I'm using to parse the content...

function sx_decorate_keyword($content){
    $keyword = "blue widgets";
    $d = new DOMDocument();
    $d->loadHTML($content);
    $x = new DOMXpath($d);
    foreach($x->query("//text()[
       contains(.,$keyword')
       and not(ancestor::h1) 
       and not(ancestor::h2) 
       and not(ancestor::h3) 
       and not(ancestor::h4) 
       and not(ancestor::h5) 
       and not(ancestor::h6)]") as $node){
        //need to wrap bold tags around the first instance of the keyword, then exit the routine
    }  
return $content;
}

Upvotes: 0

Views: 499

Answers (2)

salathe
salathe

Reputation: 51950

As Dmitri mentioned, just work on the first text node only. The example below takes the approach of dissecting the DOMText node containing your keyword(s) and wrapping the first occurrence within a <b> element.

$nodes = $x->query("... your xpath ...");
if ($nodes && $nodes->length) {
    $node = $nodes->item(0);
    // Split just before the keyword
    $keynode = $node->splitText(strpos($node->textContent, $keyword));
    // Split after the keyword
    $node->nextSibling->splitText(strlen($keyword));
    // Replace keyword with <b>keyword</b>
    $replacement = $d->createElement('b', $keynode->textContent);
    $keynode->parentNode->replaceChild($replacement, $keynode);
}

Reference:

Upvotes: 2

Dmitri
Dmitri

Reputation: 36300

You can just break out of the loop using the break;

Or you can not use foreach and instead just work on the first element only.

    $Matches = $x->query("//text()[
           contains(.,$keyword')
           and not(ancestor::h1) 
           and not(ancestor::h2) 
           and not(ancestor::h3) 
           and not(ancestor::h4) 
           and not(ancestor::h5) 
           and not(ancestor::h6)]");

if($Matches && $Matches->length > 0){
  $myText = $Matches->item(0);
  // now do you thing with $myText like create <b> element, append $myText as child,
  // replaceNode $myText with new <b> node
}

Not sure if this will work, but something like that...

Upvotes: 0

Related Questions