I am working on some Puppeteer powered website analytics and would really need to list all events on a page.
It's easy with "normal" JavaScript, so I thought I could just evaluate it in the Puppeteer and get to other tasks.
Well - it is not so easy and "getEventListeners" is not working for example. So this code below is not working (but if I take the code that gets valuated, copy it to browser's console and run - it works well);
exports.getDomEventsOnElements = function (page) {
return new Promise(async (resolve, reject) => {
try {
let events = await page.evaluate(() => {
let eventsInDom = [];
const elems = document.querySelectorAll('*');
for(i = 0; i < elems.length; i++){
const element = elems[i];
const allEventsPerEl = getEventListeners(element);
if(allEventsPerEl){
const filteredEvents = Object.keys(allEventsPerEl).map(function(k) {
return { event: k, listeners: allEventsPerEl[k] };
})
if(filteredEvents.length > 0){
eventsInDom.push({
el: element,
ev: filteredEvents
})
}
}
}
return eventsInDom;
})
resolve(events);
} catch (e) {
reject(e);
}
})
}
I've investigated further and it looks like this will not work in Puppeteer and even tried with good old JQuery's const events = $._data( element[0], 'events' );
but it does not work either.
Then I stumbled upon Chrome DevTools Protocol (CDP) and there it should be possible to get it by defining a single element on beforehand;
const cdp = await page.target().createCDPSession();
const INCLUDE_FN = true;
const { result: {objectId} } = await cdp.send('Runtime.evaluate', {
expression: 'foo',
objectGroup: INCLUDE_FN ?
'provided' : // will include fn
'' // wont include fn
});
const listeners = await cdp.send('DOMDebugger.getEventListeners', { objectId });
console.dir(listeners, { depth: null });
(src: https://github.com/puppeteer/puppeteer/issues/3349)
But this looks too complicated when I would like to check each and every DOM element for events and add them to an array. I suspect there is a better way than looping the page elements and running CDP for each and every one. Or better said - I hope :)
Any ideas?
I would just like to have an array of all elements with (JS) events like for example:
let allEventsOnThePage : [
{el: "blutton", events : ["click"]},
{el: "input", events : ["click", "blur", "focus"]},
/* you get the picture */
];