De volta ao blog

Validação de Entrada de Formulário com o Navegador de Coleta Sem Desperdício

Daniel Kim
Daniel Kim

Lead Scraping Automation Engineer

11-Aug-2026

TL;DR:

  • Atribuir element.value diretamente não dispara eventos algum. Em uma execução medida, a atribuição direta produziu uma lista de eventos vazia enquanto locator.fill() produziu focus, beforeinput e input — o valor fica no DOM de qualquer maneira, então a falha é silenciosa.
  • A atribuição direta ainda satisfaz a validação de restrição HTML5 e ainda preenche FormData. Definir value não é rejeitado pelo navegador; é simplesmente não observado, e o que quebra é qualquer lógica de página que reage a eventos.
  • Os campos que realmente quebram são os que outros campos dependem. Definir o valor de um <select> diretamente deixou a entrada dependente oculta e o botão de envio desabilitado, enquanto selectOption() revelou ambos.
  • Os seletores de data do JavaScript podem descartar o que você digitou. Em um campo de data público, preencher e, em seguida, pressionar Escape ou clicar fora retornou o campo a uma string vazia; cometer com Enter produziu o formato normalizado do widget.
  • setInputFiles() funciona mesmo que o navegador não esteja na sua máquina. Playwright transmite o arquivo local para a sessão remota, e o upload aparece nos dados do formulário enviado.
  • Erros de validação são legíveis sem capturas de tela. validity, validationMessage e um requestSubmit() bloqueado expõem exatamente qual restrição falhou e por quê.
  • Livre para começar. Novas contas Scrapeless incluem tempo de execução gratuito do Scraping Browser — inscreva-se em app.scrapeless.com.

Por que um campo preenchido ainda pode ser um formulário vazio

Um campo de formulário pode conter o texto certo e ainda assim não enviar nada útil. O valor está no DOM, uma captura de tela parece correta, e a página se comporta como se o campo nunca tivesse sido tocado — o próximo campo nunca aparece, o botão de envio permanece desabilitado, ou uma mensagem de validação que a página renderiza nunca desaparece.

A causa não é o valor. São os eventos que nunca foram despachados juntamente com ele. Formulários modernos raramente leem valores DOM brutos no momento do envio; eles se inscrevem em input e change, mantêm sua própria cópia do estado e dirigem tudo o mais a partir dessa cópia. Escreva em .value e você atualiza o DOM enquanto deixa todos os assinantes desatentos.

Este guia mede esse comportamento em vez de afirmá-lo, e depois passa pelas classes de widgets onde a lacuna realmente aparece: seletores dependentes, seletores de data do JavaScript, entradas de arquivo e formulários cujas restrições rejeitam o envio totalmente. Tudo abaixo é executado no Playwright conectado ao Scrapeless Scraping Browser, um navegador em nuvem personalizável e anti-detecção alimentado pelo Chromium desenvolvido internamente. Se você precisar dos fundamentos de preenchimento e envio primeiro, o guia de envio de formulário Puppeteer cobre esse terreno.


Pré-requisitos

  • Node.js 18 ou mais recente
  • Uma conta Scrapeless e uma chave de API — inscreva-se em app.scrapeless.com
  • Conhecimento prático de seletores CSS e do modelo de eventos DOM

Instalar

O navegador é remoto, então playwright-core é tudo o que você precisa — sem download do Chromium empacotado:

bash Copy
pnpm add playwright-core@1.56.1

Defina sua chave de API a partir do ambiente em vez de colocá-la no código fonte:

bash Copy
export SCRAPELESS_API_KEY="paste-your-key-here"

O Scraping Browser fala o Protocolo DevTools do Chrome, então connectOverCDP é o ponto de entrada. O formulário de teste público abaixo pertence ao projeto Selenium e existe especificamente para prática de automação:

js Copy
import { chromium } from 'playwright-core';

const endpoint = 'wss://browser.scrapeless.com/api/v2/browser?' + new URLSearchParams({
  token: process.env.SCRAPELESS_API_KEY,
  sessionTTL: '300',
});

const browser = await chromium.connectOverCDP(endpoint);
const context = browser.contexts()[0];
const page = context.pages()[0] || await context.newPage();

await page.goto('https://www.selenium.dev/selenium/web/web-form.html', {
  waitUntil: 'domcontentloaded',
});

console.log('chromium:', browser.version());
console.log('title:', await page.title());
console.log('form method:', await page.getAttribute('form', 'method'));
await browser.close();
text Copy
chromium: 140.0.7339.35
title: Web form
form method: get

Uma nota específica para sessões remotas: a página padrão impõe Tipos Confiáveis, então page.setContent() é rejeitado antes que possa escrever marcação. Navegar para uma URL data: é a alternativa funcional, e a medição abaixo usa exatamente isso.


Implementação básica: medir o que cada técnica emite

Em vez de confiar em uma regra geral, instrumente a entrada. Este fixture registra todos os eventos que atingem o campo e também renderiza sua própria mensagem de validação de um listener input — o padrão que torna falhas silenciosas visíveis:

js Copy
import { chromium } from 'playwright-core';

const endpoint = 'wss://browser.scrapeless.com/api/v2/browser?' + new URLSearchParams({
  token: process.env.SCRAPELESS_API_KEY,
  sessionTTL: '300',
});
const browser = await chromium.connectOverCDP(endpoint);
const context = browser.contexts()[0];
const page = context.pages()[0] || await context.newPage();

const fixture = `<!doctype html><meta charset=utf-8><title>fixture</title>
<form id=f>
  <input id=t name=t type=text required minlength=4>
  <output id=err></output>
</form>
<script>
window.__ev = [];
var el = document.getElementById('t');
['focus','beforeinput','input','change','keydown','keyup'].forEach(function(type){
  el.addEventListener(type, function(){ window.__ev.push(type); });
});
el.addEventListener('input', function(){
  document.getElementById('err').textContent = el.validity.valid ? 'ok' : 'too short';
});
</script>`;
const fixtureUrl = 'data:text/html;charset=utf-8,' + encodeURIComponent(fixture);

async function trial(name, action) {
  await page.goto(fixtureUrl, { waitUntil: 'load' });
  await action();
  await page.waitForTimeout(200);
  const state = await page.evaluate(() => ({
    events: window.__ev.slice(),
    formData: new FormData(document.getElementById('f')).get('t'),
    valid: document.getElementById('t').validity.valid,
    errorUi: document.getElementById('err').textContent,
  }));
  console.log(name);
  console.log('  events  :', JSON.stringify(state.events));
  console.log('  formData:', JSON.stringify(state.formData), '| valid:', state.valid);
  console.log('  error-UI:', JSON.stringify(state.errorUi));
}

await trial('A: el.value = "hello"', () =>
  page.$eval('#t', el => { el.value = 'hello'; }));

await trial('B: el.value + dispatchEvent(input)', () =>
  page.$eval('#t', el => {
    el.value = 'hello';
    el.dispatchEvent(new Event('input', { bubbles: true }));
  }));

await trial('C: locator.fill("hello")', () => page.fill('#t', 'hello'));

await trial('D: locator.pressSequentially("hello")', () =>
  page.locator('#t').pressSequentially('hello'));

await browser.close();
text Copy
A: el.value = "hello"
  events  : []
  formData: "hello" | valid: true
  error-UI: ""
B: el.value + dispatchEvent(input)
  events  : ["input"]
  formData: "hello" | valid: true
  error-UI: "ok"
C: locator.fill("hello")
  events  : ["focus","beforeinput","input"]
  formData: "hello" | valid: true
  error-UI: "ok"
D: locator.pressSequentially("hello")
  events  : ["focus","keydown","beforeinput","input","keyup","keydown","beforeinput","input","keyup","keydown","beforeinput","input","keyup","keydown","beforeinput","input","keyup","keydown","beforeinput","input","keyup"]
  formData: "hello" | valid: true
  error-UI: "ok"

Leia o primeiro caso cuidadosamente. A atribuição direta produziu uma lista de eventos vazia, e ainda assim FormData trouxe "hello" e o campo se reportou como válido. O valor está genuinamente lá. O que nunca aconteceu é a própria reação da página: a saída de erro permaneceu vazia porque nada disse para ela atualizar.

Esse resultado recontextualiza o conselho usual. Definir .value não é inerentemente quebrado — é invisível. Um formulário HTML simples sem script o envia corretamente. Um formulário cujo comportamento está conectado a eventos trata o campo como não tocado, e o sintoma aparece em outro lugar totalmente.
fill() produz a sequência compacta que a maioria das páginas precisa: foco, então beforeinput, então input. pressSequentially() produz a sequência completa de teclado por caractere, que só importa quando a página inspeciona pressionamentos de tecla individuais, como um autocompletar que consulta a cada letra. A especificação de Eventos UI do W3C define a ordem que essas técnicas imitam, e o Padrão DOM do WHATWG define como um evento enviado manualmente se propaga — incluindo a isTrusted flag que o separa de uma ação real do usuário.

Obtenha sua chave de API no plano gratuito: app.scrapeless.com


Padrões avançados: os widgets que precisam de mais do que um valor

Seleções que desbloqueiam outros campos

Aqui é onde a atribuição direta deixa de ser meramente invisível e começa a custar dados. O fixture abaixo revela um segundo campo e ativa o botão de envio a partir do manipulador de change da seleção:

js Copy
import { chromium } from 'playwright-core';

const endpoint = 'wss://browser.scrapeless.com/api/v2/browser?' + new URLSearchParams({
  token: process.env.SCRAPELESS_API_KEY,
  sessionTTL: '300',
});
const browser = await chromium.connectOverCDP(endpoint);
const context = browser.contexts()[0];
const page = context.pages()[0] || await context.newPage();

const fixture = `<!doctype html><meta charset=utf-8><title>conditional</title>
<form id=f>
  <select id=plan name=plan>
    <option value="">choose</option>
    <option value="basic">Basic</option>
    <option value="team">Team</option>
  </select>
  <div id=extra hidden><input id=seats name=seats type=number></div>
  <button id=go type=submit disabled>Continue</button>
</form>
<script>
var plan = document.getElementById('plan');
plan.addEventListener('change', function(){
  document.getElementById('extra').hidden = (plan.value !== 'team');
  document.getElementById('go').disabled = !plan.value;
});
</script>`;
const url = 'data:text/html;charset=utf-8,' + encodeURIComponent(fixture);

async function trial(name, action) {
  await page.goto(url, { waitUntil: 'load' });
  await action();
  await page.waitForTimeout(200);
  console.log(name, '->', JSON.stringify(await page.evaluate(() => ({
    selectValue: document.getElementById('plan').value,
    seatsVisible: !document.getElementById('extra').hidden,
    submitEnabled: !document.getElementById('go').disabled,
  }))));
}

await trial('A: select.value = "team"', () =>
  page.$eval('#plan', el => { el.value = 'team'; }));

await trial('B: select.value + dispatchEvent(change)', () =>
  page.$eval('#plan', el => {
    el.value = 'team';
    el.dispatchEvent(new Event('change', { bubbles: true }));
  }));

await trial('C: locator.selectOption("team")', async () => {
  await page.selectOption('#plan', 'team');
  await page.waitForSelector('#seats', { state: 'visible' });
  await page.fill('#seats', '25');
});

await browser.close();
text Copy
A: select.value = "team" -> {"selectValue":"team","seatsVisible":false,"submitEnabled":false}
B: select.value + dispatchEvent(change) -> {"selectValue":"team","seatsVisible":true,"submitEnabled":true}
C: locator.selectOption("team") -> {"selectValue":"team","seatsVisible":true,"submitEnabled":true}

O Caso A é a falha que vale a pena lembrar. A seleção realmente contém "team", então um script que verifica seu próprio trabalho lendo o valor de volta relata sucesso — enquanto o campo de assentos que deveria preencher ainda está escondido e o botão que deveria clicar ainda está desativado. selectOption() evita toda a classe de problemas porque despacha input e change da mesma forma que uma seleção do usuário.

Caixas de seleção e rádios

Use check() e uncheck() em vez de click(). Eles afirmam o estado resultante em vez de alternar cegamente, então uma caixa que já contém checked na marcação não é invertida da forma errada:

js Copy
await page.check('#my-check-2');
await page.uncheck('#my-check-1');
await page.check('#my-radio-2');

Seletores de data que descartam o que você digitou

Um campo que parece um input de data é frequentemente um input de texto com um calendário JavaScript anexado, e o calendário possui o valor. Seis maneiras de inserir a mesma data em um campo de data público produziram quatro valores armazenados diferentes:

Técnica Valor do campo depois Sobreposição
fill('2026-08-06') "2026-08-06" ainda aberto
fill(...) então Escape "" fechado
fill(...) então clique em outro lugar "" fechado
fill(...) então Enter "08/06/2026" ainda aberto
el.value = '2026-08-06' "2026-08-06" nunca aberto
pressSequentially('2026-08-06') então Escape "10/08/6" fechado

Três dessas são armadilhas. Descartar a sobreposição com Esc ou clicando em outro lugar retornou o campo a uma string vazia, porque o widget trata o descarte como uma edição abandonada. Digitar caractere por caractere permitiu que o calendário reformattasse o campo durante a entrada e produziu um valor que não corresponde a nada. A atribuição direta manteve o texto literal apenas porque o widget nunca interagiu — o que significa que o estado interno do calendário e o campo discordam.

Comprometer-se com Enter é o encerramento que funciona, e o widget reescreve o valor em seu próprio formato de exibição. Verifique o valor comprometido em vez de assumir que sua entrada sobreviveu:

js Copy
await page.fill('input[name="my-date"]', '2026-08-06');
await page.press('input[name="my-date"]', 'Enter');
console.log(await page.inputValue('input[name="my-date"]'));
// 08/06/2026

Onde um verdadeiro <input type="date"> é usado em vez disso, a imagem é mais simples: o valor deve ser uma string ISO YYYY-MM-DD independentemente do formato mostrado ao usuário.

Uma preocupação razoável com um navegador em nuvem é que os uploads de arquivos podem não funcionar, uma vez que o arquivo vive em sua máquina e o navegador não. setInputFiles() lida com a transferência pelo protocolo, então nenhum diálogo do sistema operacional é envolvido:

js Copy
import fs from 'node:fs';
import { chromium } from 'playwright-core';

fs.writeFileSync('upload-sample.txt', 'scrapeless form upload fixture\n');

const endpoint = 'wss://browser.scrapeless.com/api/v2/browser?' + new URLSearchParams({
  token: process.env.SCRAPELESS_API_KEY,
  sessionTTL: '300',
});
const browser = await chromium.connectOverCDP(endpoint);
const context = browser.contexts()[0];
const page = context.pages()[0] || await context.newPage();

await page.goto('https://www.selenium.dev/selenium/web/web-form.html', {
  waitUntil: 'domcontentloaded',
});

await page.setInputFiles('input[name="my-file"]', './upload-sample.txt');

console.log(await page.$eval('input[name="my-file"]', el => ({
  files: el.files.length,
  name: el.files[0].name,
  hasBytes: el.files[0].size > 0,
})));

await Promise.all([
  page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  page.click('button[type="submit"]'),
]);
console.log('submitted my-file:',
  JSON.stringify(new URL(page.url()).searchParams.get('my-file')));
await browser.close();
text Copy
{ files: 1, name: 'upload-sample.txt', hasBytes: true }
submitted my-file: "upload-sample.txt"

A sessão remota recebeu o arquivo, o FileList está populado, e o nome do arquivo aparece nos dados do formulário submetido.

Formulários de múltiplos passos

Trate cada passo como uma pré-condição, em vez de um atraso. Após comprometer o campo que avança o formulário, aguarde um controle que só existe no próximo passo antes de tocá-lo:

js Copy
await page.selectOption('#plan', 'team');
await page.waitForSelector('#seats', { state: 'visible' });
await page.fill('#seats', '25');

Aguardar pelo elemento em si mantém o script vinculado ao verdadeiro estado do formulário, que é o mesmo princípio que faz o caso da seleção condicional funcionar.


Leia o estado submetido de volta

A única confirmação autorizada é o que o servidor recebeu. Este formulário é enviado via GET, então os valores aceitos vão para a string de consulta da próxima página:

js Copy
import { chromium } from 'playwright-core';

const endpoint = 'wss://browser.scrapeless.com/api/v2/browser?' + new URLSearchParams({
  token: process.env.SCRAPELESS_API_KEY,
  sessionTTL: '300',
});
const browser = await chromium.connectOverCDP(endpoint);
const context = browser.contexts()[0];
const page = context.pages()[0] || await context.newPage();

await page.goto('https://www.selenium.dev/selenium/web/web-form.html', {
  waitUntil: 'domcontentloaded',
});

await page.fill('#my-text-id', 'Ada Lovelace');
await page.selectOption('select[name="my-select"]', '2');
await page.check('#my-check-2');
await page.uncheck('#my-check-1');
await page.check('#my-radio-2');
await page.fill('input[name="my-date"]', '2026-08-06');
await page.press('input[name="my-date"]', 'Enter');

await Promise.all([
  page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  page.click('button[type="submit"]'),
]);

const params = Object.fromEntries(new URL(page.url()).searchParams.entries());
for (const key of ['my-text', 'my-select', 'my-check', 'my-date']) {
  console.log(key.padEnd(10), JSON.stringify(params[key]));
}
await browser.close();
text Copy
my-text    "Ada Lovelace"
my-select  "2"
my-check   "on"
my-date    "08/06/2026"

Verificar a visão do servidor diz que o formulário foi aceito, não apenas que os campos foram preenchidos. Observe que my-date carrega o formato comprometido do widget, não a string que foi digitada.


Lendo erros de validação a partir do DOM

Quando uma submissão é rejeitada, o navegador já possui uma explicação estruturada. A API de validação de restrição no Padrão HTML expõe qual regra falhou e a mensagem que o navegador exibiria, portanto, não há necessidade de capturar a tela da página e ler pixels:

js Copy
import { chromium } from 'playwright-core';

const endpoint = 'wss://browser.scrapeless.com/api/v2/browser?' + new URLSearchParams({
  token: process.env.SCRAPELESS_API_KEY,
  sessionTTL: '300',
});
const browser = await chromium.connectOverCDP(endpoint);
const context = browser.contexts()[0];
const page = context.pages()[0] || await context.newPage();

const fixture = `<!doctype html><meta charset=utf-8><title>validation</title>
<form id=f>
  <input id=email name=email type=email required>
  <input id=code name=code type=text required minlength=4>
  <button type=submit>Send</button>
</form>
<script>
window.__submitted = false;
document.getElementById('f').addEventListener('submit', function(e){
  e.preventDefault(); window.__submitted = true;
});
</script>`;
await page.goto('data:text/html;charset=utf-8,' + encodeURIComponent(fixture),
  { waitUntil: 'load' });

await page.fill('#email', 'not-an-address');
await page.fill('#code', 'ab');

const report = await page.evaluate(() => {
  const form = document.getElementById('f');
  form.requestSubmit();
  return {
    submitted: window.__submitted,
    fields: [...form.elements]
      .filter(el => el.willValidate && !el.checkValidity())
      .map(el => ({
        name: el.name,
        message: el.validationMessage,
        failed: Object.keys(ValidityState.prototype)
          .filter(k => k !== 'valid' && el.validity[k]),
      })),
  };
});
console.log(JSON.stringify(report, null, 1));
await browser.close();
text Copy
{
 "submitted": false,
 "fields": [
  {
   "name": "email",
   "message": "Please include an '@' in the email address. 'not-an-address' is missing an '@'.",
   "failed": [
    "typeMismatch"
   ]
  },
  {
   "name": "code",
   "message": "Please lengthen this text to 4 characters or more (you are currently using 2 characters).",
   "failed": [
    "tooShort"
   ]
  }
 ]
}

submitted é false porque o navegador bloqueou a submissão antes que o manipulador fosse executado, e cada campo com falha nomeia sua própria regra quebrada. Isso lhe dá uma razão legível por máquina para registrar em vez de uma ausência inexplicada de resultados. A referência MDN sobre validação de formulários do lado do cliente cobre o conjunto mais amplo de restrições às quais essas flags se mapeiam.

Páginas que renderizam seu próprio texto de erro em vez de confiar no navegador são o caso que a primeira medição já explicou: essas mensagens aparecem apenas se os eventos para os quais ouvem foram disparados.


Toda técnica acima é uma Playwright comum e funciona contra um Chromium local também. A documentação de entrada do Playwright descreve os mesmos métodos. O que um navegador local não lhe oferece é um ambiente consistente para que o formulário seja avaliado.

Os formulários são a parte de um site mais intimamente ligada a controles de fraude e abuso, e esses controles analisam a sessão em vez da marcação: o endereço de saída, a impressão digital e se o navegador se apresenta como uma instalação genuína. Essa é a parte que o Scrapeless Scraping Browser fornece: um navegador em nuvem personalizável e anti-deteção, alimentado por Chromium desenvolvido internamente, configurado por sessão no momento da conexão através de parâmetros como sessionTTL, tudo por trás da mesma chamada connectOverCDP que seu código já utiliza. Nada na manipulação de widgets muda; apenas o ambiente. A página de produto do Scraping Browser e a documentação cobrem toda a superfície da sessão.


Solução de Problemas

Sintoma Causa Solução
O campo mantém o valor, mas nada mais na página reage A atribuição direta de .value não disparou eventos Use fill(), ou dispare input e change explicitamente
O campo dependente nunca aparece O manipulador change que o revela nunca foi executado Use selectOption(), depois aguarde o seletor dependente
O botão de envio permanece desabilitado A página o habilita a partir de um evento que você não emitiu Conduza o campo controlador com um método de localizador
O campo de data está vazio após preenchê-lo O seletor tratou o fechamento como uma edição abandonada Confirme com Enter e leia o valor de volta
O valor da data parece embaralhado O calendário reformatou o campo entre as teclas pressionadas Use fill() mais Enter em vez de digitação por caractere
page.setContent() é rejeitado A página padrão impõe Tipos Confiáveis Navegue até uma URL data: em vez disso
A submissão não faz nada em silêncio A validação de restrição a bloqueou Leia validationMessage e validity para cada campo

Conclusão

Problemas de automação de formulários que parecem misteriosos são geralmente uma coisa mensurável: um valor que existe no DOM sem os eventos que fazem a página notar. A atribuição direta define o valor, satisfaz a validação de restrição e preenche FormData — e não informa ninguém. Métodos de localizador como fill, check, e selectOption emitem o que a página está ouvindo, razão pela qual continuam funcionando quando um formulário cresce um campo dependente, um widget seletor, ou uma camada de validação.

Crie o hábito de confirmar o resultado em vez da entrada: leia o valor confirmado de volta de um widget, aguarde o elemento que uma alteração deveria revelar, e inspecione validity quando uma submissão não vai a lugar nenhum. Rodar no Scrapeless Scraping Browser mantém o ambiente consistente por baixo de tudo isso, então a única variável restante é sua lógica de seleção.


Pronto para Construir Seu Pipeline de Dados com Poder da IA?

Junte-se à comunidade para reivindicar um plano gratuito e comparar notas com desenvolvedores que automatizam fluxos de trabalho baseados em formulários: Discord · Telegram.
Inscreva-se em app.scrapeless.com para obter tempo de execução gratuito do Scraping Browser e veja preços quando você aumentar a escala.


FAQ

Q: Definir element.value diretamente funciona alguma vez?
Sim, para formulários que leem o DOM no momento da submissão. Na execução medida, a atribuição direta populou FormData e passou na validação de restrições. Ela falha sempre que a página mantém uma cópia própria do estado ou renderiza algo de um input ou change listener, porque a atribuição não despacha eventos.

Q: Quais eventos eu preciso despachar se eu tiver que definir um valor manualmente?
Despache um evento input em bolha para campos de texto e um evento change em bolha para selects, checkboxes e radios. Alguns frameworks rastreiam o valor anterior no elemento e ignoram um evento cujo valor acreditam que já gravaram; chamar o setter nativo do protótipo antes de despachar evita isso. Usar fill(), check() ou selectOption() contorna completamente o problema.

Q: Por que meu campo de data está vazio após eu preenchê-lo?
Um widget de calendário JavaScript provavelmente reverteu isso. Preencher o campo e então pressionar Escape ou clicar em outro lugar retornou uma string vazia nos testes, porque o widget trata uma sobreposição descartada como uma edição abandonada. Confirme o valor com Enter e leia-o de volta antes de enviar.

Q: Posso fazer o upload de um arquivo quando o navegador está rodando na nuvem?
Sim. setInputFiles() transfere o arquivo local para a sessão remota através do protocolo, então nenhum diálogo de arquivo do sistema operacional é envolvido. O FileList populado e os dados do formulário enviados confirmam ambos o upload.

Q: Como posso descobrir por que um formulário se recusou a enviar?
Itere os elementos do formulário, filtre aqueles onde checkValidity() retorna falso e leia as flags validationMessage e validity de cada um. Isso lhe dará a restrição específica que falhou, como typeMismatch ou tooShort, sem inspecionar pixels renderizados.

Q: Devo usar fill() ou digitar caractere por caractere?
Prefira fill(). Ele emite foco, beforeinput e input, que é o que quase todos os formulários escutam. Só busque a entrada por caractere quando a página reage a digitações individuais, como um autocomplete que faz consultas em cada letra — e verifique o resultado, já que alguns widgets reformulam o campo entre as digitações.

Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.

Artigos mais populares

Catálogo