1

问题

我有一个相同节点的列表,这些节点具有相同的处理程序或附加到它的函数。

我需要单击 DOM 中存在的每个交互节点(这是功能或要求)。

如果节点已被单击,或者节点具有相同的处理程序,我想跳过要单击的节点。

请在屏幕截图下方找到参考。其中事件侦听器有一个带有处理程序名称的 click 事件。我们可以获取点击处理程序或函数的名称吗?

我正在使用DOMDebugger.getEventListeners({ objectId: remoteObjectId }),但它不会返回handler and originalHandler不为什么。

在此处输入图像描述

4

2 回答 2

1

这里提到了一个怪癖:您需要objectGroup设置才能获取处理程序。以下是一些获取名称的方法:

const html = `
  <!doctype html>
  <html>
    <head>
      <meta charset='UTF-8'>
      <title>Test</title>
      <script>
        function main() {
          document.body.addEventListener('click', logClick);
        }

        function logClick() {
          console.log('click');
        }
      </script>
    </head>
    <body onload='main();'>Text.</body>
  </html>`;

const puppeteer = require('puppeteer');

(async function main() {
  try {
    const browser = await puppeteer.launch();
    const [page] = await browser.pages();

    await page.goto(`data:text/html,${html}`);

    const cdp = await page.target().createCDPSession();

    const nodeObject = (await cdp.send('Runtime.evaluate', {
      expression: "document.querySelector('body')",
      objectGroup: 'foobar',
    })).result;

    const listenerObject = (await cdp.send('DOMDebugger.getEventListeners', {
      objectId: nodeObject.objectId,
    })).listeners[0].handler;

    const listenerName1 = (await cdp.send('Runtime.callFunctionOn', {
      functionDeclaration: 'function() { return this.name; }',
      objectId: listenerObject.objectId,
      returnByValue: true,
    })).result.value;

    const listenerName2 = (await cdp.send('Runtime.getProperties', {
      objectId: listenerObject.objectId,
      ownProperties: true,
    })).result.find(property => property.name === 'name').value.value;

    await cdp.send('Runtime.releaseObject', { objectId: listenerObject.objectId });
    await cdp.send('Runtime.releaseObject', { objectId: nodeObject.objectId });
    await cdp.send('Runtime.releaseObjectGroup', { objectGroup: 'foobar' });

    console.log(listenerName1);
    console.log(listenerName2);

    await browser.close();
  } catch (err) {
    console.error(err);
  }
})();

输出:

logClick
logClick

UPD 匿名处理程序:

const puppeteer = require('puppeteer');

(async function main() {
  try {
    const browser = await puppeteer.launch();
    const [page] = await browser.pages();

    await page.goto('https://jqueryui.com/resources/demos/datepicker/inline.html');

    const cdp = await page.target().createCDPSession();

    const nodeObject = (await cdp.send('Runtime.evaluate', {
      expression: "document.querySelector('a.ui-datepicker-next')",
      objectGroup: 'foobar',
    })).result;

    const listenerObject = (await cdp.send('DOMDebugger.getEventListeners', {
      objectId: nodeObject.objectId,
    })).listeners[0].handler;

    console.log(listenerObject);

    await cdp.send('Runtime.releaseObject', { objectId: listenerObject.objectId });
    await cdp.send('Runtime.releaseObject', { objectId: nodeObject.objectId });
    await cdp.send('Runtime.releaseObjectGroup', { objectGroup: 'foobar' });

    await browser.close();
  } catch (err) {
    console.error(err);
  }
})();

输出:

{
  type: 'function',
  className: 'Function',
  description: 'function( e ) {\n' +
    '\n' +
    '\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n' +
    '\t\t\t\t// when an event is called after a page has unloaded\n' +
    '\t\t\t\treturn typeof jQuery !== "undefined" &&\n' +
    '\t\t\t\t\t( !e || jQuery.event.triggered !== e.type ) ?\n' +
    '\t\t\t\t\tjQuery.event.dispatch.apply( eventHandle.elem, arguments ) :\n' +
    '\t\t\t\t\tundefined;\n' +
    '\t\t\t}',
  objectId: '{"injectedScriptId":3,"id":2}'
}
于 2020-07-23T20:31:55.043 回答
0

如果元素在您的网站中是静态的而不是动态的,我建议您提供一个解决方案。因为这个元素有一个公共选择器,你可以使用 this.page.$$ 并获取所有这些元素的数组。之后用 {button: elementHandle, isClicked: } 创建一个对象,这样你就可以知道节点是否被点击。我希望此解决方案对您有所帮助。

page.$$ - https://pptr.dev/#?product=Puppeteer&version=v5.2.1&show=api-pageselector-1

备注:如果选择器有更多事件或发生变化,你可以知道它是否被点击。例如颜色。

于 2020-07-23T16:50:09.783 回答