【问题标题】:Puppeteer: How to listen to object eventsPuppeteer:如何监听对象事件
【发布时间】:2017-11-04 04:44:05
【问题描述】:

是否可以监听页内对象发送的事件?假设我去的页面中有这段代码:

var event = new CustomEvent('status', { detail: 'ok' });
window.addEventListener('status', function(e) {
  console.log('status: ', e.detail);
});
setInterval(window.dispatchEvent, 1000, event);

我希望能够监听窗口对象(或任何其他 JS 对象)发送的事件。如何在 Puppeteer 中做到这一点?

【问题讨论】:

标签: javascript google-chrome headless puppeteer


【解决方案1】:

首先,您必须公开一个可以从页面内调用的函数。其次,监听事件并调用暴露的函数并传递事件数据。

// Expose a handler to the page
await page.exposeFunction('onCustomEvent', ({ type, detail }) => {
    console.log(`Event fired: ${type}, detail: ${detail}`);
});

// listen for events of type 'status' and
// pass 'type' and 'detail' attributes to our exposed function
await page.evaluateOnNewDocument(() => {
    window.addEventListener('status', ({ type, detail }) => {
        window.onCustomEvent({ type, detail });
    });
});

await page.goto(/* ... */);

如果现在在页面中触发了一个事件status(就像您提供的代码一样),您会看到它已记录在您的 Node.js 应用程序的控制台中。

正如 cmets 中所发布的,可以在 puppeteer examples 中找到更复杂的示例。

【讨论】:

  • 您好。按照这里解释的简单方法,我在冗长的页面渲染结束时调用我的静态 html: var event = new Event("sceneReady"); window.dispatchEvent(事件);但是在 NodeJS 代码对应处永远无法到达事件侦听器。
【解决方案2】:

如果你想真正等待自定义事件,你可以这样做。

const page = await browser.newPage();

/**
  * Attach an event listener to page to capture a custom event on page load/navigation.
  * @param {string} type Event name.
  * @return {!Promise}
  */
function addListener(type) {
  return page.evaluateOnNewDocument(type => {
    // here we are in the browser context
    document.addEventListener(type, e => {
      window.onCustomEvent({ type, detail: e.detail });
    });
  }, type);
}

const evt = await new Promise(async resolve => {
  // Define a window.onCustomEvent function on the page.
  await page.exposeFunction('onCustomEvent', e => {
    // here we are in the node context
    resolve(e); // resolve the outer Promise here so we can await it outside
  });
  await addListener('status'); // setup listener for "status" custom event on page load
  await page.goto('http://example.com');  // N.B! Do not use { waitUntil: 'networkidle0' } as that may cause a race condition
});

console.log(`${evt.type} fired`, evt.detail || '');

https://github.com/puppeteer/puppeteer/blob/main/examples/custom-event.js 的示例为基础

【讨论】:

    【解决方案3】:

    更简单的方法await waitForEvent('event-name', 10)

    /**
     * Wait for the browser to fire an event (including custom events)
     * @param {string} eventName - Event name
     * @param {integer} seconds - number of seconds to wait.
     * @returns {Promise} resolves when event fires or timeout is reached
     */
    async function waitForEvent(eventName, seconds) {
    
        seconds = seconds || 30;
    
        // use race to implement a timeout
        return Promise.race([
    
            // add event listener and wait for event to fire before returning
            page.evaluate(function(eventName) {
                return new Promise(function(resolve, reject) {
                    document.addEventListener(eventName, function(e) {
                        resolve(); // resolves when the event fires
                    });
                });
            }, eventName),
    
            // if the event does not fire within n seconds, exit
            page.waitForTimeout(seconds * 1000)
        ]);
    }
    

    Source

    【讨论】:

    • 最佳答案 IMO
    • 你如何使用它?我在做:```等待 page.goto('localhost:1234');等待 waitForEvent('myCustomEvent'); ``` 我可以添加一个自定义事件处理程序并看到我的事件肯定会被触发,但这个承诺永远不会解决。
    • 没关系,我使用window.dispatchEvent 来触发我的事件,而不是document.dispatchEvent
    • 我遇到的一个小故障对我来说并不明显:看起来Promise.race 并没有取消waitForTimeout 的承诺。这意味着如果您在waitForEvent 中有 30 秒的超时,那么您的 puppeteer 脚本将在等待超时承诺解决的同时运行至少 30 秒,即使事件已触发。我没有看到一个明显/简单的方法来解决这个问题,所以我刚刚停止在我的脚本中使用超时。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-13
    • 2013-09-05
    • 1970-01-01
    • 1970-01-01
    • 2012-12-10
    • 2018-02-13
    • 1970-01-01
    相关资源
    最近更新 更多