3

I have a chat app with status represented by this line:

<span id='status'>Offline</span> 

and I want Puppeteer to log every time the text within this span changes.

Say, in the beginning the status was "Offline", then it was changed to "Online", and then after some time to "Away" and so on. I want Puppeteer to capture those moments and the status (Offline>Online>Away)

What I managed to do is the following:

const page = await browser.newPage();
await page.goto('https://chat.com');
const statusHandle = await page.$('span#status');
let statusText = await page.evaluate(() => document.querySelector('#status').innerText);
let iniVal = {statusHandle,statusText };

at this point I have the statusHandle and the initial status.

Now, my understanding (as per 1 and 2) is that I need to combine the two in

await page.waitForFunction(
  (iniVal) => iniVal.statusHandle.innerHTML !== iniVal.statusText, 
  { timeout: 0 }, 
  iniVal
) 

and place it in a loop. That's the point, where I'm struggling.

First it gives a type error "TypeError: Converting circular structure to JSON", which is due to the passed key value pairs not being primitives, but even when I oversimplify and just do (as per 1):

await page.waitForFunction(
  'document.querySelector("span#status").inner‌​Text === "Online"'
)

it yields nothing.

To sum up: I am looking to

  1. Make Puppeteer evaluate the change in document.querySelector('#status').innerText !== statusText

  2. Return the new status value

  3. Have the process run in a loop

laggingreflex
  • 32,948
  • 35
  • 141
  • 196
denis
  • 53
  • 1
  • 4

2 Answers2

3

I'd just set a recursive function with a callback:

async function monitor (selector, callback, prevValue) {
  const newVal = await page.$(selector);
  if (newVal !== prevValue) {
    callback(newVal);
  }
  /* add some delay */
  await new Promise(_ => setTimeout(_, 1000))
  /* call recursively */
  monitor (selector, callback, newVal);
}
monitor('span#status', status => {
  // Fires whenever `status` changes
})
laggingreflex
  • 32,948
  • 35
  • 141
  • 196
  • 1
    PS: Haven't tried myself but this might help too: https://stackoverflow.com/questions/12421491/can-i-monitor-changes-on-an-html-div – laggingreflex May 17 '18 at 14:23
  • Thank you& sorry for the delay. I went (after slight modification) for a recursive function, works like a charm. For people reading - consider implementing an autorestart after N tries, as (per my readup) there is a possibility of a memory leak, also to avoid timeout error on fetching a page (happened to me once), do the following => await page.goto('http://target.url', {waitUntil: 'domcontentloaded'}); – denis Jun 02 '18 at 06:40
  • how can we view changed HTML, TEXT? – Jah May 18 '20 at 13:03
0

Rather than polling with recursion and setTimeout, better use MutationObserver like in this answer.

niutech
  • 28,923
  • 15
  • 96
  • 106