Measure Interaction to Next Paint (INP) with automated clicking on all elements

203 views Asked by At

I prepared a javascript code which you can run in Chrome Console. It clicks on all elements on page which are links or buttons.

Then it outputs a table with CSS classes of those elements and a time it took for a website to resnpond to user click.

THE PROBLEM While clicking on links and buttons, eventually script clicks on an element that contains link to other pages, causing page to refresh and erase my data.

How can I prevent this script from opening any kind of links and causing page to refresh? I tried excluding href and src attributes, but still there were some websites for which it didn't work.

EXAMPLES It works here and doesn't open any links: https://www.theguardian.com/football/2024/jan/08/how-the-guardian-ranked-the-100-best-female-footballers-in-the-world-2023

It doesn't work here and always open links: https://www.upwork.com/hire/python-developers/

The script:

async function measureINP() {
const results = [];
const processedElements = new Set();

function simulateClick(element) {
    return new Promise((resolve, reject) => {
        function interactionHandler(event) {
            // Prevent default action to keep the page stable
            event.preventDefault();
            const startTime = performance.now();

            requestAnimationFrame(() => {
                const endTime = performance.now();
                const inp = endTime - startTime;
                element.removeEventListener('click', interactionHandler);
                resolve({ element: element, inp: inp });
            });
        }

        // Add a timeout to reject the promise if no response in 5 seconds
        const timeoutId = setTimeout(() => {
            element.removeEventListener('click', interactionHandler);
            reject(new Error(`Timeout waiting for response from ${element.tagName}`));
        }, 5000);

        setTimeout(() => {
            element.addEventListener('click', interactionHandler);
            element.click();
        }, 100); // Wait for 100ms before triggering the click event

        // Clean up the timeout when the interaction handler is called
        element.addEventListener('click', () => clearTimeout(timeoutId), { once: true });
    });
}

const elements = document.querySelectorAll('a, button, input[type="button"], input[type="submit"], .menu-button');
for (const element of elements) {
    const elementIdentifier = `${element.tagName}${element.id ? '#' + element.id : ''}${element.className ? '.' + element.className.replace(/\s+/g, '.') : ''}`;

    // Define a list of keywords and attributes that commonly indicate language-changing buttons
    const languageKeywords = ['language', 'lang', 'translate'];
    const languageAttributes = ['data-lang', 'data-translate'];

    // Check if the element matches any of the language indicators
    const elementTextContent = element.textContent.toLowerCase();
    const elementAttributes = Array.from(element.attributes).map(attr => attr.name.toLowerCase());
    const isLanguageButton =
        languageKeywords.some(keyword => elementTextContent.includes(keyword)) ||
        languageAttributes.some(attribute => elementAttributes.includes(attribute));

    // Check if the element is a language-changing button
    if (isLanguageButton) {
        console.log(`Skipping language-changing button: ${elementIdentifier}`);
        continue;
    }

    // Skip if this element has already been processed
    if (processedElements.has(elementIdentifier)) {
        continue;
    }

    try {
        const data = await simulateClick(element);
        processedElements.add(elementIdentifier); // Add to the set after processing
        results.push({
            element: elementIdentifier,
            interaction: 'Click',
            "INP (milliseconds)": Number(data.inp.toFixed(2)) // Rename key to "INP (milliseconds)"
        });
        await new Promise((resolve) => setTimeout(resolve, 100)); // Wait for 100ms before the next interaction
    } catch (error) {
        console.error(`Error with element ${element.tagName}:`, error);
    }
}

console.table(results); } measureINP();
0

There are 0 answers