Konstantin Bodnia
Konstantin Bodnia

Reputation: 1523

Puppeteer detect when the new tab is opened and get page object

My web app opens a new tab under some conditions. But when I try to get all tabs (await browser.pages()) I get only one back, the initial page.

How can I get the new page's object in my code?

This happens when you don't create new tab with puppeteer with await browser.newPage(), but when you do something like this:

await (await browser.pages())[0].evaluate(() => {
    window.open('http://www.example.com', '_blank');
});

The page won't be available in the browser.pages() response.

Upvotes: 18

Views: 29500

Answers (3)

ggorlen
ggorlen

Reputation: 57115

An alternative to this (perfectly fine!) approach is to promisify the browser's "targetcreated" event as described in a comment on Puppeteer issue #386:

const puppeteer = require("puppeteer");
const { once } = require('events');

(async () => {
  const html = `<a href="https://news.ycombinator.com" target="_blank">click</a>`;
  const browser = await puppeteer.launch({headless: false});
  const [page] = await browser.pages(); 
  await page.setContent(html);
  await page.waitForSelector("a", {visible: true});
  console.log((await browser.pages()).length); // => 1

  const newPagePromise = once("targetcreated", browser).then(x => x.page());
  await page.click("a");
  const newPage = await newPagePromise;

  console.log((await browser.pages()).length); // => 2
  console.log((await newPage.content()));
  await browser.close();
})();

Another way to write this is with Promise.all and a destructuring assignment to get the first element from the result array:

const [newPage] = await Promise.all([
  once(browser, "targetcreated").then(x => x.page),
  page.click("a")
]);

Upvotes: 0

Robert Hillen
Robert Hillen

Reputation: 551

This code will catch the new page in a new tab if it was opened by clicking a link in the original page.

//save target of original page to know that this was the opener:     
const pageTarget = page.target();
//execute click on first tab that triggers opening of new tab:
await page.click('#selector');
//check that the first page opened this new page:
const newTarget = await browser.waitForTarget(target => target.opener() === pageTarget);
//get the new page object:
const newPage = await newTarget.page();

Upvotes: 55

Md. Abu Taher
Md. Abu Taher

Reputation: 18826

It's hard without knowing your conditions when the app opens a new tab. It works perfectly fine for me. Here is a code demonstrating how I can use it. Read the comments to understand the steps.

UPDATED:

window.open() doesn't return a promise, thus browser.pages() is executed faster than the browser can create and report the event. We can use the targetcreated event to know if any new tab is created.

browser.on('targetcreated', function(){
    console.log('New Tab Created');
})

If you wait for a while or return a promise, you will see it reports it within browser.pages() count.

await tabOne.evaluate(() => {
    window.open('http://www.example.com', '_blank');
  });
await tabOne.waitFor(2000); // await for a while
console.log("current page count ", (await browser.pages()).length); // 3

Here is the final code.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();

  browser.on('targetcreated', function(){
    console.log('New Tab Created');
  })

  // get current tab count
  console.log("current page count ", (await browser.pages()).length); // 3

  // create a new tab
  await browser.newPage();
  // lets see if tab increased
  console.log("current page count ", (await browser.pages()).length); // 3

  // use destructuring for easier usage
  const [tabOne, tabTwo] = (await browser.pages());

  // use the tabs aka Page objects properly
  await tabOne.goto('https://example.com');
  console.log("Tab One Title ",await tabOne.title()); // Example Domain

  // use the tabs aka Page objects properly
  await tabTwo.goto('https://example.com');
  console.log("Tab Two Title ",await tabTwo.title()); // Example Domain

  await tabOne.evaluate(() => {
    window.open('http://www.example.com', '_blank');
  });
  await tabOne.waitFor(2000); // wait for a while
  console.log("current page count ", (await browser.pages()).length); // 3

  // close the browser
  await browser.close();
})();

If you run it, you'll get the result in following sequence.

/*
current page count  1
New Tab Created
current page count  2
Tab One Title  Example Domain
Tab Two Title  Example Domain
New Tab Created
current page count  3
*/

Upvotes: 7

Related Questions