How to click on element with specific width puppeteer

Viewed 474

I'm having a problem clicking on an element, my script tries to click on this element before the label and add to cart button are visible, and I don't know why this happens since I passed the visible:true parameter I believe I think this shouldn't happen, however, to fix this problem I thought of clicking on the element when it had a specific width, but I don't know how to do it, look at my script below:

const puppeteer = require('puppeteer');         
const fs = require('fs').promises;                                                                                                              

(async () => {                                    
 try{                                              
  console.log("Started!")                        
  const browser = await 
  puppeteer.launch({         
   executablePath: '/usr/bin/chromium',            
   //headless:false,                               
   args:['--no-sandbox', '--disable-gpu']        
  });                                             
  const page = await browser.newPage();   
  await page.setRequestInterception(true);        
  const blockedResourceTypes = ["image", "bacon", "imageset", "stylesheet", "font", "texttrack", "csp_report", "media", "object", "sub_frame", "main_frame"]
  const allowURLs = [/*Index.css*/"https://images.lojanike.com.br/site/ni/dist/css/Index.css?v=5b95e1dc7eff61bf78f523058eb924e6",                                   ]
  const allowedRequest = req => !blockedResourceTypes.includes(req.resourceType()) || allowURLs.includes(req.url())                               
  page.on('request', (req) => {
   if (allowedRequest(req)) {                       
    req.continue();
   }
   else {
    req.abort();
   }                                             
  });                                             
  const cookiesString = await fs.readFile('./cookies.json');
  const cookies = JSON.parse(cookiesString);
  await.page.setCookie(...cookies);
  await page.goto('https://www.nike.com.br/air-max-1-x-clot-infantil-24-33-67-80-445-308385', { waitUntil:'domcontentloaded', timeout:0});
  const tamanho = await page.$x('//label[@for="tamanho__id32"]', {visible:true})                  
  await tamanho[0].click('//label[@for="tamanho__id32"]');                                        
  await page.waitForSelector('button#btn-comprar')                                   
  page.click('button#btn-comprar')                
  console.log("Added to car!")         
 }                                                
 catch(err){
  console.log("Erro, elemento não está contido na pagina ou erro inesperado!", err)}     
})();

How can I click on the label for example when it has a width of 32px? I tried it that way and it didn't work:

tamanho = await page.$x('//label[@for="tamanho__id32"]', {visible:true, width:60})

How can I click on an element that has a specific width?

Detail:

If I put load instead of domcontentloaded it works, the problem is that load delays my script as it will have to wait for the page to load, so I need to click on an element that has a specific width, something like the code I tried, and it works lol

4 Answers

You can set an interval to check a certain condition:

const label = document.querySelector('label[for="tamanho__id32"]');

const _interval = setInterval(() => {
    const width = label.offsetWidth;

    if (width === 32) {
        label.click();
        clearInterval(_interval);
    }
}, 200);

// clear interval after some time                    
setTimeout(() => {
    clearInterval(_interval);
}, 10000); 

Use a recursive function:

async function search(page) {
  try {
    const tamanho = await page.$("label[for='tamanho__id32']");
    const box = await tamanho.boundingBox();

    if (box.width == 32) {
      await tamanho.click();
    } else {
      throw new Error();
    }
  } catch (error) {
    await page.waitForTimeout(1000);

    return search();
  }
}

It will check every second whether the selector and the proper width is there. It uses the boundingBox method to get the elements width. Just define it outside your code and await it instead of your XPath-Selector.

You can use page.waitForSelector(selector[, options]) - and attach the listener to your .launch() promise

puppeteer.launch({         
   executablePath: '/usr/bin/chromium',            
   //headless:false,                               
   args:['--no-sandbox', '--disable-gpu']        
  }).then(async browser => {
  const page = await browser.newPage();
  page
    .waitForSelector('label[for="tamanho__id32"]')
    .then(() => {
        page.goto('https://www.nike.com.br/air-max-1-x-clot-infantil-24-33-67-80-445-308385');
     }
    browser.close();
});

function to wait for the button to be a specific width before taking an action:

function waitForCondition(obj, len, callBack){    
  window.setTimeout(function(){
    if(obj.width() >= len){
      callBack();
    }else{
      waitForCondition(obj, len, callBack);
    }
  },250);
}

   var obj=$("#myButton");
     waitForCondition( obj , 80, function(){
        alert("threshold met");
   }); 

jsfiddle

Related