Howdy_McGee
Howdy_McGee

Reputation: 10673

Match All Elements With Background Colors

Here's the issue - in a TinyMCE I'm working with it's adding a background-color on a span wrapper instead of the parent element. I'm trying to search the content string and grab the span styles to put it on the parent element. I have HTML like this:

<h3><span style="background-color: #000000;">Hello World!</span></h3>

So I'm trying to match any span with a background or background-color and put that as a style onto its parent. I'm trying to work with DOMDocument but I'm not too familiar with it:

$html       = '<h3><span style="background-color: #000000;">Hello World!</span></h3>';
$htmlDOM    = new DOMDocument();
$htmlDOM->loadHTML( $html );
$xpath      = new DOMXPath( $htmlDOM );
$headers = $xpath->query( '//h1//span|//h2//span|//h3//span|//h4//span|//h5//span|//h6//span' );

foreach( $headers as $index => $element ) {
    print_r( $element );    // The Header Element

    if( $element->attributes->length ) {
        foreach( $element->attributes as $asdf => $attr ) {
            print_r( $attr );   // The Span Attributes ( style )
        }
    }
}

I get the actual style attribute with the above but I don't know how to assign it to the parent. I'm not sure if this is faster than preg_match() but I'm not familiar enough with regex. Any help / suggestions or tips would be appreciated!

Upvotes: 1

Views: 114

Answers (3)

splash58
splash58

Reputation: 26153

$html       = '<h3><span style="background-color: #000000;">Hello World!</span></h3>';
$htmlDOM    = new DOMDocument();
$htmlDOM->loadHTML( $html );
$xpath      = new DOMXPath( $htmlDOM );
// Find `h*` having child `span` with `style` attribute    
$headers = $xpath->query( '//*[contains("h1|h2|h3|h4|h5|h6", name())  and span[@style]]');

foreach( $headers as $element) {
    // Find `span` element
    $span = $xpath->query('./span', $element)->item(0);
    // Move all attributes from `span` to `h*
    foreach($span->attributes as $attr)
       $element->setAttribute($attr->name, $attr->nodeValue);
    // Save value of `span`
    $value = $element->nodeValue;
    // Remove `span` 
    $span->parentNode->removeChild($span);
    // Set former `span` value as `h*` value
    $element->nodeValue = $value;
}

echo $htmlDOM->saveHTML();

demo

Upvotes: 2

Howdy_McGee
Howdy_McGee

Reputation: 10673

Thanks to /u/splash58's answer it pushed me in the right direction:

$html       = '<h3><span style="background-color: #000000;">Hello World!</span></h3>';
$htmlDOM    = new DOMDocument();
$htmlDOM->loadHTML( $html );
$xpath      = new DOMXPath( $htmlDOM );
$elements   = $xpath->query( '//h1//span|//h2//span|//h3//span|//h4//span|//h5//span|//h6//span' );

if( $elements->length ) {
    foreach( $elements as $span ) {
        $span_style = $span->getAttribute( 'style' );

        if( false !== strpos( $span_style, 'background' ) ) {
            $parent_style   = $span->parentNode->getAttribute( 'style' );
            $parent_style   = rtrim( $parent_style, ';' );
            $span_style     = rtrim( $span_style, ';' );

            $new_style      = "{$parent_style}; {$span_style};";
            $parent_style   = $span->parentNode->setAttribute( 'style', $new_style );
            $span->parentNode->nodeValue = $span->nodeValue;
        }
    }
}

echo $htmlDOM->saveHTML();

One thing I'm not certain on is why the span gets removed when I didn't specifically remove it. It seems to happen after I assign the spans nodeValue to the parent. If anyone has an idea on why this might be or clarification please comment below and let me know!

Upvotes: 0

xystum
xystum

Reputation: 1009

A dom solution would be better. But if you want to use a regex solution, you could use:

$html     = '<h3><span style="background-color: #000000;">Hello World!</span></h3>';
$html_new = preg_replace("@(<h\d[^>]*)(>)(\s*<span[^<>]+)(style\s*=\s*["'][^"']*["'])[^>]*>([^<>]+)</span>@", "$1 $4$2$5", $html);

It will only work on h1 etc. elements, and if the span element will follow directely and has no child elements.

Upvotes: 0

Related Questions