How to use DOM element within client script in UI Builder

Hari1
Mega Sage

Hi,

I am trying to run the below code within client script in UI Builder.

function handler({
    api,
    event,
    helpers,
    imports
}) {
    helpers.timing.setTimeout(function() {
        var script = this.document.createElement('script');
        script.src='https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js';
        script.onload = function() {
            takeScreenshot();
        };
        this.document.head.appendChild(script);
    });

    function takeScreenshot() {
        html2canvas(this.document.body, {
            onrendered: function(canvas) {
                var canvasVal = canvas.shadowRoot;
                var dataURL = canvas.toDataURL("image/png");
                var link = this.document.createElement("a");
                link.href = dataURL;
                link.download = "screenshot.png";
                link.click();
            }
        });
    }
}

I see an error message stating "html2canvas is not a function" in the browser log. Thanks.

15 REPLIES 15

@Sheldon  Swift/ @Brad Tilton 
Here is the updated code by accessing the shadow DOM.

helpers.timing.setTimeout(function() {
        if (typeof this.document !== 'undefined') {
            var script = this.document.createElement('script');
            script.src='https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js';
            script.onload = function() {
                console.log('html2canvas is loaded and ready to use.');

                this.window.document.querySelector('macroponent-f51912f4c700201072b211d4d8c26010').shadowRoot.querySelector('sn-canvas-appshell-main').shadowRoot.querySelector('macroponent-c276387cc331101080d6d3658940ddd2').shadowRoot.querySelector('sn-canvas-main').shadowRoot.querySelector('sn-canvas-screen').shadowRoot.querySelector('macroponent-fba9fd6793430210ec8c32edfaba10f0').shadowRoot.querySelector('now-button').shadowRoot.querySelector('button').onclick = takeScreenshot();

                function takeScreenshot() {
                    html2canvas(this.document.body, {
                        onrendered: function(canvas) {
                            var canvasVal = canvas.shadowRoot;
                            var dataURL = canvas.toDataURL("image/png");
                            var link = this.document.createElement("a");
                            link.href = dataURL;
                            link.download = "screenshot.png";
                            link.click();
                        }
                    });
                }
            };
            this.document.head.appendChild(script);
        } else {
            console.error('Document is undefined.');
        }
    });

 But i still see the html2canvas has an error and not code is not working on the UI Builder form. Can you please help!

Brad Tilton
ServiceNow Employee
ServiceNow Employee

We don't really support accessing the DOM from UI Builder. I think a custom component is your best bet here. Unfortunately, I don't have any step by step guides for this use case, but you can start here: https://developer.servicenow.com/dev.do#!/reference/next-experience/washingtondc/ui-framework/gettin... 

Hi @Hari1 - The underlying issue is that html2canvas is loaded in the main document's DOM, while the button is located within a shadow DOM. This separation means that html2canvas is not defined in the context where your takeScreenshot() function is executed.

 

Also, there's no reason for any of that querySelector code. You can define what happens when the button is clicked in UI builder:

SheldonSwift_1-1724937157741.png

@Sheldon  Swift, Yes that is exactly what i am trying to do. I am calling the client script in the UI Builder from the Button Clicked event. Please refer the below snapshot.

Hari1_0-1724950147995.png

Below is the client script that i am using in the UI Builder:

 

function handler({
    api,
    event,
    helpers,
    imports
}) {

    helpers.timing.setTimeout(function() {
        var script = this.document.createElement('script');
        script.src='https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js';
        script.onload = function() {
            takeScreenshot();
        };
        this.document.head.appendChild(script);
    });

    function takeScreenshot() {
        helpers.timing.setTimeout(function() {
        html2canvas(this.document.body, {
            onrendered: function(canvas) {
                var dataURL = canvas.toDataURL("image/png");
                var link = this.document.createElement("a");
                link.href = dataURL;
                link.download = "screenshot.png";
                link.click();
            }
        });
        });
    }
}

 

And i see the error: html2canvas is not a function

Can you please help. Please let me know if i am doing anything wrong.

Ravi Gaurav
Giga Sage
Giga Sage

Hi @Hari1 
I guess the error the version of html2canvas you are trying to load is quite old (0.4.1).
can you try the below :-

function handler({ api, event, helpers, imports }) {
helpers.timing.setTimeout(function () {
var script = document.createElement('script');
script.src='https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js'; // Updated to a newer version
script.onload = function () {
takeScreenshot();
};
document.head.appendChild(script);
});

function takeScreenshot() {
// Ensure html2canvas is loaded
if (typeof html2canvas === 'function') {
html2canvas(document.body).then(function (canvas) {
var dataURL = canvas.toDataURL("image/png");
var link = document.createElement("a");
link.href = dataURL;
link.download = "screenshot.png";
link.click();
}).catch(function (error) {
console.error('Screenshot capture failed:', error);
});
} else {
console.error('html2canvas failed to load or is not available.');
}
}
}

--------------------------------------------------------------------------------------------------------------------------


If you found my response helpful, I would greatly appreciate it if you could mark it as "Accepted Solution" and "Helpful."
Your support not only benefits the community but also encourages me to continue assisting. Thank you so much!

Thanks and Regards
Ravi Gaurav | ServiceNow MVP 2025,2024 | ServiceNow Practice Lead | Solution Architect
CGI
M.Tech in Data Science & AI

ï”— YouTube: https://www.youtube.com/@learnservicenowwithravi
ï”— LinkedIn: https://www.linkedin.com/in/ravi-gaurav-a67542aa/