1

I am Using Joomla CMS, and I want to manipulate the DOM in render. I want to modify the inline scripts to execute when the page has loaded. But I have problems with a Google maps script that has HTML tags in its code, when I try to get the content of the script using nodeValue, it cuts it off at the first closing of the </div>; I also tried not to modify anything just load the HTML and then save it with saveHTML() and the same problem happens.

Part of page code (page.html)

<!DOCTYPE html>
<html>
<head>
    <title>Page title</title>
    <script src="https://maps.googleapis.com/maps/api/js?key=XXXXXX" type="text/javascript"></script>
</head>
<body>
<header></header>
<main>
    <div class="wrapper">
        <div class="inner">
            <div class="map-container">
                <div class="google-map">
                    <div id="map-canvas95" class="map-canvas" style="width:100%;height:400px">
                        <script type="text/javascript">
                            //API demos Used(synchronous loading, info window,)
                            var myLatlng95 = new google.maps.LatLng(-11.926755,-77.05359);
                            var mapOptions95 = {
                                scrollwheel: false,
                                zoom: 15,
                                center: myLatlng95,
                                disableDefaultUI: true,
                                mapTypeId: google.maps.MapTypeId.ROADMAP,
                                mapTypeControl: true,
                                mapTypeControlOptions: {
                                    style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
                                    position: google.maps.ControlPosition.LEFT_TOP
                                },
                                panControl: true,
                                panControlOptions: {
                                    position: google.maps.ControlPosition.LEFT_CENTER
                                },
                                zoomControl: true,
                                zoomControlOptions: {
                                    style: google.maps.ZoomControlStyle.SMALL,
                                    position: google.maps.ControlPosition.LEFT_CENTER
                                },
                                scaleControl: true,
                                streetViewControl: true,
                                streetViewControlOptions: {
                                    position: google.maps.ControlPosition.LEFT_CENTER
                                }
                            };
                            var map95 = new google.maps.Map(document.getElementById('map-canvas95'), mapOptions95);

                            //Info Window
                            var contentString95 = '<div id="content">'+
                                '<div id="siteNotice"></div>'+
                                '<h1 id="firstHeading" class="firstHeading">Market Title</h1>'+
                                '<div id="bodyContent">'+
                                    '<p>Graphic Design. Lorem ipsum dolor sit amet, consectetur adipiscing elit. of Quisque ultricies vestibulum molestie.</p>'+
                                '</div>'+
                            '</div>';
                            var infowindow95 = new google.maps.InfoWindow({
                                content: contentString95,
                                maxWidth: 300
                            });
                            
                            //Marker
                            var marker95 = new google.maps.Marker({
                                position: myLatlng95,
                                map: map95,
                                title: 'Market Title'           });

                            //Event for open Info Window
                            google.maps.event.addListener(marker95, 'click', function() {
                                infowindow95.open(map95,marker95);
                            });
                        </script>
                    </div>
                </div>
            </div>
        </div>
    </div>
</main>
<footer></footer>
</body>
</html>

My DOM Script

    //$app = JFactory::getApplication('site'); for Joomla only
    //$body = $app->getBody(false);//for Joomla only
    $body = file_get_contents('page.html');
    $domDoc = new DOMdocument();
    libxml_use_internal_errors(true);
    $domDoc->loadHTML($body);
    libxml_clear_errors();
    $scripts =$domDoc->getElementsByTagName('script');
    $openScript = "\nwindow.addEventListener('DOMContentLoaded', function() {";
    $closeScript = "});\n";
    foreach ($scripts as $script) {
        $newScript = $openScript.$script->nodeValue.$closeScript;
        $script->nodeValue = (!empty($script->nodeValue)) ? $newScript : $script->nodeValue;
    }
    //$body = $domDoc->saveHTML();  for Joomla  
    //$app->setBody($body);for Joomla
    $domDoc->saveHTMLFIle('newDom.html');

In result file you can see script cutted

<script>
  window.addEventListener('DOMContentLoaded', function() {    
  var contentString<?php echo $uniqid; ?> = '<div id="content">'+
  '<div id="siteNotice"></div>;
  });
</script>

Like this Google maps script, I have seen other scripts that have HTML tags in their code. How do I solve this?. Thanks.

5
  • 1
    At least in HTML 4.01 times, the general recommendation was that any </ occurring within the content of a script element, should be escaped as <\/, to avoid any parser problems. I am guessing the occurrence of </ trips the PHP DOM parser up as well here. “I want to modify the inline scripts to execute when the page has loaded.” - can you explain which way exactly you are doing that? The code indicates that you are trying to “clone” the script element, but what are you then eventually doing with it? Commented Nov 27, 2020 at 8:11
  • @CBroe Thanks for your answer, I have added more details to the code and also the result to better understand what I am trying to do. It works well with almost all scripts except those with HTML tags. The Google maps code does not belong to me, it is an extension. Commented Nov 27, 2020 at 14:51
  • @Leo so that I am sure that I am testing on correct input data, can I have the $body value? This is important to the minimal reproducible example. Commented Nov 28, 2020 at 13:52
  • @mickmackusa I have updated the code to be able to test directly from PHP, without the need for Joomla. Commented Nov 28, 2020 at 18:48
  • Maybe a different DOM parser library could be an option. stackoverflow.com/questions/4029341/… Commented Nov 30, 2020 at 7:26

1 Answer 1

2

HTML5 is not supported by ext/dom. You can use the HTML5-PHP library. It reads the HTML5 into an XHTML DOM.

use \Masterminds\HTML5;

$html5 = new HTML5();
$document = $html5->loadHTML(getHTML());
$xpath = new DOMXpath($document);
// register namespace prefix for Xpath expressions 
$xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');

foreach ($xpath->evaluate('//xhtml:div[@class="google-map"]//xhtml:script') as $script) {
    $script->textContent = "\nwindow.addEventListener('DOMContentLoaded', function() {\n{$script->textContent}\n});\n";
}
echo $html5->saveHTML($document);

Also, do not use DOMNode::$nodeValue but DOMNode::$textContent to ensure proper handling of special characters.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, I will read more about this, because I also need to add attributes to the images and links.
Is there an advantage in using evaluate() versus query() in this case? @ThW
Not for this expression. I just never use query() because it does not support all expressions. (Only node list results, not scalars).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.