Jsdom: implementação de innerText

Criado em 25 set. 2015  ·  26Comentários  ·  Fonte: jsdom/jsdom

jsdom é uma ótima ferramenta para web scraping. No entanto, o textContent é uma maneira muito inconveniente de obter texto legível para conversão html2text.

Há um artigo maravilhoso sobre a utilidade de innerText insignificante em muitos casos:

http://perfectionkills.com/the-poor-misunderstood-innerText/

O autor sugere getSelection().toString() como uma solução muito lenta, mas getSelection ainda não está implementado no jsdom .

Você poderia considerar uma implementação do innerText no jsdom ? O autor fez uma grande exploração sobre isso, ele até adicionou uma especificação simples no final.

feature layout

Comentários muito úteis

Caso alguém mais esteja enfrentando esse problema, dei um passo adiante e usei o pacote sanitize-html para obter basicamente o que o navegador está fazendo (observe que não importei a configuração do JSDOM porque achei que não era necessário ao colocar isso no meu arquivo de configuração do Jest, mas se você não estiver usando o Jest, precisará usar a configuração global.Element = (new JSDOM()).window.Element que @bennypowers recomendou):

Object.defineProperty(global.Element.prototype, 'innerText', {
  get() {
    return sanitizeHtml(this.textContent, {
      allowedTags: [], // remove all tags and return text content only
      allowedAttributes: {}, // remove all tags and return text content only
    });
  },
  configurable: true, // make it so that it doesn't blow chunks on re-running tests with things like --watch
});

Todos 26 comentários

E que pena que a biblioteca rangy Selection e innerText não é compatível com jsdom : https://github.com/timdown/rangy/issues/348

Portanto, innerText não é padrão e não é implementado em pelo menos um mecanismo principal (Firefox). Sem um padrão, não acho que deveríamos implementá-lo.

Parece que há algum movimento nessa coisa toda com uma especificação de rascunho aqui . Veja também todas as referências . No entanto, não há problemas no repositório, então me pergunto o quão completo ele já está / quão rápido será o progresso.

Pelas especificações, parece que não podemos implementar innerText corretamente sem suporte básico de layout.

Sim, isso não será realmente implementável no jsdom de qualquer maneira, sem muito trabalho de infraestrutura ... ninguém tem esperanças :(.

Quanto ao requisito de suporte de layout: https://github.com/rocallahan/innerText-spec/issues/2

Existe algum plano para implementá-lo devido à adoção do WHATWG?

Sim... Embora a especificação exija muitas coisas que o jsdom não tem, em torno de caixas CSS :(. Não tenho certeza do que fazer.

Existe alguma lib para isso conectar junto com o jsdom?

@domenic se importa em deixar um pouco de conhecimento sobre por que essa é uma revisão de infraestrutura? Nós pensamos que o gorila de 800 libras no quarto deixaria lo-key. Mas parece que não vai a lugar nenhum. Como você sabe, tenho envolvido minha cabeça nas entranhas do jsdom. Onde seria um ótimo lugar no repositório para começar a revisar o código para um jsdom newb?

Obrigado desde já 🙏 /cc @vsemozhetbyt

O principal problema é o fato de que innerText se apóia no mecanismo de layout para orientação e o jsdom não possui mecanismo de layout. Consulte https://html.spec.whatwg.org/multipage/dom.html#the -innertext-idl-attribute e
http://perfectionkills.com/the-poor-misunderstood-innerText/ . Do segundo link:

Observe como innerText representa quase exatamente como o texto aparece na página. textContent, por outro lado, faz algo estranho - ignora novas linhas criadas por
e em torno de elementos estilizados como blocos ( neste caso).

Ainda fora do escopo e sem solução alternativa?

Aparentemente, a especificação diz:

Se este elemento não estiver sendo renderizado, ou se o agente do usuário for um agente do usuário não CSS, [ênfase adicionada] então retorne o mesmo valor que o atributo textContent IDL neste elemento.

Eu acho que uma solução alternativa seria simplesmente retornar textContent .

Implementamos CSS suficiente que acho que não se aplica. Nós simplesmente não implementamos as partes do layout...

Olá pessoal, alguma novidade sobre isso?

Basta usar o cromo sem cabeça :)

@domenic daquela especificação que @coreh mencionou:
https://html.spec.whatwg.org/multipage/dom.html#the -innertext-idl-attribute

Se esse elemento não estiver sendo renderizado ou se o agente do usuário for um agente do usuário não CSS, retorne o mesmo valor que o atributo textContent IDL neste elemento.

https://html.spec.whatwg.org/multipage/rendering.html#being -rendered

Um elemento está sendo renderizado se tiver caixas de layout CSS associadas, caixas de layout SVG ou algum equivalente em outras linguagens de estilo.

Se jsdom não implementar as partes do layout, isso não significa que "não sendo renderizado" se aplica?

Esta mensagem é para qualquer pessoa que esteja acessando este thread do github que deseja apenas uma maneira de fazer seus testes passarem sem alterar suas implementações de função.

copypasta para o topo de seus arquivos de teste:

// Expose JSDOM Element constructor
global.Element = (new JSDOM()).window.Element;
// 'Implement' innerText in JSDOM: https://github.com/jsdom/jsdom/issues/1245
Object.defineProperty(global.Element.prototype, 'innerText', {
  get() {
    return this.textContent;
  },
});

Naturalmente, as ressalvas da discussão acima se aplicam.

Caso alguém mais esteja enfrentando esse problema, dei um passo adiante e usei o pacote sanitize-html para obter basicamente o que o navegador está fazendo (observe que não importei a configuração do JSDOM porque achei que não era necessário ao colocar isso no meu arquivo de configuração do Jest, mas se você não estiver usando o Jest, precisará usar a configuração global.Element = (new JSDOM()).window.Element que @bennypowers recomendou):

Object.defineProperty(global.Element.prototype, 'innerText', {
  get() {
    return sanitizeHtml(this.textContent, {
      allowedTags: [], // remove all tags and return text content only
      allowedAttributes: {}, // remove all tags and return text content only
    });
  },
  configurable: true, // make it so that it doesn't blow chunks on re-running tests with things like --watch
});

eu tinha uma necessidade semelhante, mas queria ir um pouco mais longe do que apenas usar o textContent - novamente, isso não será uma representação precisa do que os navegadores realmente fazem, especialmente no que diz respeito aos elementos ocultos pelo css, mas é bom suficiente para o meu caso de uso:

function innerText(el)
  el = el.cloneNode(true) // can skip if mutability isn't a concern
  el.querySelectorAll('script,style').forEach(s => s.remove())
  return el.textContent
}

Que pena!

Aparentemente, a especificação diz:

Se este elemento não estiver sendo renderizado, ou se o agente do usuário for um agente do usuário não CSS , [ênfase adicionada] então retorne o mesmo valor que o atributo textContent IDL neste elemento.

Eu acho que uma solução alternativa seria simplesmente retornar textContent.

Implementamos CSS suficiente que acho que não se aplica. Nós simplesmente não implementamos as partes do layout...

@domenic, por favor, considere uma interpretação mais liberal da especificação

textContent é explicitamente permitido como fallback, quando a aplicação de regras CSS é muito cara

também, innerText é especificado como getter e setter

Dado que sou o editor de especificações, posso afirmar com certeza que "quando a aplicação de regras CSS é muito cara" não é o que a especificação está dizendo.

.. essa foi a minha interpretação de "se o agente do usuário for um agente do usuário não CSS"

qual é a diferença entre um "agente de usuário CSS" e um "agente de usuário não CSS"?

A respeito:
um agente de usuário CSS pode "aplicar regras CSS" e produzir o resultado (gráfico ou textual)
um agente de usuário não CSS é muito burro para "aplicar regras CSS"

Implementamos CSS suficiente que acho que não se aplica.

o que você quer dizer? window.getComputedStyle?

um fallback para textContent ainda é melhor do que não implementar uma interface padrão

Talvez possamos usar o textContent para substituir o resultado de innerText enquanto executamos testes com jsdom . Por exemplo:

describe('mytest', () => {
  beforeAll(() => {
    Object.defineProperty(HTMLElement.prototype, 'innerText', {
      get() {
        return this.textContent;
      }
    });
  });
  it('should ok', () => {
  // test assertions
  });
});
Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

lehni picture lehni  ·  4Comentários

camelaissani picture camelaissani  ·  4Comentários

potapovDim picture potapovDim  ·  4Comentários

cg433n picture cg433n  ·  3Comentários

vsemozhetbyt picture vsemozhetbyt  ·  4Comentários