How to interact with menu items using Puppeteer in Node.js

I’m working on automating a school portal using Puppeteer to extract some information. After logging into the main page, I need to click on one of the menu options but I keep running into issues. The page has a navigation menu with multiple items and I specifically want to click the one that says “View My Current Enrollments” which is the 5th option.

I’ve tried different approaches like targeting the span element, anchor tag, and using various selectors but I get errors saying the selector doesn’t exist or is undefined. Even though I can see the element in the browser inspector, Puppeteer can’t seem to find it. I also attempted using mouse coordinates but that requires a valid selector first.

Here’s the HTML structure I’m dealing with:

<div class="navigation-menu">
  <ul class="menu-grid">
    <li class="menu-item">
      <a href="/student/rollnumber"><i class="icon-calendar"></i>
      <span>Check Roll Number</span></a>
    </li>
    <li class="menu-item">
      <a href="/student/feedback"><i class="icon-survey"></i>
      <span>Student Feedback</span></a>
    </li>
    <li class="menu-item">
      <a href="/student/records"><i class="icon-document"></i>
      <span>Academic Records</span></a>
    </li>
    <li class="menu-item">
      <a href="/student/curriculum"><i class="icon-list"></i>
      <span>Study Program</span></a>
    </li>
    <li class="menu-item">
      <a href="/student/courses"><i class="icon-books"></i>
      <span>View My Current Enrollments</span></a>
    </li>
    <li class="menu-item">
      <a href="/finance/payment"><i class="icon-cash"></i>
      <span>Fee Payment</span></a>
    </li>
  </ul>
</div>

My current Puppeteer script looks like this:

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  
  await page.goto('https://portal.example.edu');
  
  await page.type('#userID', 'student123');
  await page.type('#userPass', 'mypassword');
  
  const captcha = await page.$('#verification img');
  await captcha.screenshot({ path: 'verify.png' });
  
  const captchaResult = await processCaptcha('verify.png');
  const solution = calculateAnswer(captchaResult);
  
  if (solution) {
    await page.type('#captchaInput', solution.toString());
  }
  
  await page.click('#loginBtn');
  await page.waitForNavigation({ waitUntil: 'networkidle2' });
  
  // This is where I'm stuck - trying to click the enrollment link
  await page.evaluate(() => {
    console.log("Menu items found:", document.querySelectorAll(".menu-item").length);
  });
})();

I’ve tried targeting by class name, link text, and element type but nothing seems to work. What’s the best way to click on that specific menu item?

Wait for the element to load first with await page.waitForSelector('.menu-item a[href="/student/courses"]') then click it. The menu often takes time to render after login and Puppeteer tries clicking before it’s ready.

Had the same problem scraping a university portal last year. The menu gets loaded by JavaScript after the page loads, so you’re clicking before it’s ready. Skip the navigation wait and use page.waitForTimeout(2000) after login instead - gives the menu time to render. Then target the span directly: await page.click('a[href="/student/courses"] span'). These portal sites love hover effects on menu items, so try await page.hover('.menu-item:nth-child(5)') before clicking. Sometimes that’s what makes the element actually clickable.

This happens because the menu’s probably loading dynamically or there’s CSS messing with visibility. I hit the same thing automating our training portal. Skip waiting for navigation - instead use page.waitForSelector('.navigation-menu', { visible: true }) right after login. This makes sure the whole menu actually loads. Then target the link directly: await page.click('a[href="/student/courses"]'). Way more reliable than spans or nth-child selectors since it works regardless of page state. Still having issues? Add { visible: true } to your click options so Puppeteer waits for the element to actually show up before clicking.