Existe um seletor de CSS para elementos que contêm determinado texto?

512

Estou procurando um seletor de CSS para a seguinte tabela:

Peter    | male    | 34
Susanne  | female  | 12

Existe algum seletor para corresponder a todos os TDs que contêm "macho"?

jantimon
fonte
1
O problema é que isso seria muito difícil de implementar de maneira eficiente.
Ms2ger 6/10/09
7
Um seletor XPath pode fazê-lo com o método .text () (se você preferir não usar o executor JavaScript).
djangofan
11
Veja um exemplo de como você pode fazer isso usando xpath: // h1 [text () = 'Session'] e pode testar o xpath no Chrome digitando $ x ("// h1 [text () = 'Session']" ) no console
VinnyG
1
Isso seria tão conveniente. Por exemplo, uma célula da tabela contendo uma marca de seleção ou a sequência "Sim", "Aprovado", "OK" etc. pode estar verde.
Andreas Rejbrand
as $xrespostas a pergunta. para a pergunta original, $x("//td[text()='male']")faz o truque
Xiao

Respostas:

394

Se eu li a especificação corretamente, não.

Você pode corresponder em um elemento, o nome de um atributo no elemento e o valor de um atributo nomeado em um elemento. Não vejo nada para corresponder conteúdo dentro de um elemento.

Dean J
fonte
228
Não é um caso extremo: :empty.
Ciro Santilli escreveu
34
Para responder à minha própria pergunta: Sim, ainda é verdade por hoje! Os seletores de conteúdo foram descontinuados! Não há mais seletores de conteúdo desde CSS3. ( w3.org/TR/css3-selectors/#selectors )
Wlad
158

Usando jQuery:

$('td:contains("male")')
moettinger
fonte
7
Acabou precisando do oposto disso, que é: jQuery (elemento) .not (": contains ('string')")
Jazzy
311
Exceto que não é CSS, é JavaScript.
Synetech
9
Concordo - é por isso que minha resposta disse que eu estava usando jQuery, não CSS. Mas essa parte da minha resposta foi editada. =)
moettinger 07/07
18
mas a palavra fe male em si também contém a palavra 'male' no? está funcionando apenas porque o homem é o primeiro? Bug esperando para acontecer se eles são reordenados?
Michael Durrant 26/03
5
@ Michael Durrant: Você brinca, mas a correspondência de cadeias de elementos cruzados (que é um problema ainda maior que as correspondências regulares de substring dentro do mesmo elemento) é, de fato, uma das maiores razões: contains () foi descartada.
BoltClock
158

Parece que eles estavam pensando sobre isso na especificação CSS3, mas não foi suficiente .

:contains()Seletor CSS3 http://www.w3.org/TR/css3-selectors/#content-selectors

Jeff Beaman
fonte
9
Looks like they were thinking about it for the CSS3 spec but it didn't make the cut.E por boas razões, isso violaria toda a premissa de separar estilo, conteúdo, estrutura e comportamento.
Synetech
56
@Synetech Na verdade, ele pode ajudar a separar o estilo do conteúdo, pois significa que o conteúdo não precisa saber sobre seu cliente será considerado importante para basear o estilo. No momento, nosso conteúdo html normalmente está muito emparelhado com o css, incluindo classes que sabemos que o estilista se importa. Eles já estão mudando para permitir CSS no conteúdo, conforme evidenciado pelos seletores de valor de atributo no CSS3.
DannyMeister
8
@ Synetech e como é "comportamento", exatamente? Para mim, é uma questão de apresentação. Ele não faz nada - apresenta certos tipos de conteúdo de uma certa maneira.
BarbaraKwarc
4
@ DannyMeister, oh, você não precisa me convencer. Alguns anos depois, eu me encontro de volta aqui, tentando procurar exatamente essa funcionalidade, e chateado por não apenas não existir, mas ter sido rejeitado. 😒 O eBay acabou de alterar a interface do usuário e tornou seu site muito difícil de usar. Estou tentando corrigi-lo com uma folha de estilo do usuário, mas a marcação deles não distingue entre leilões e listagens BIN, portanto, a única maneira de destacar o número de lances em uma lista é correspondendo o elemento pelo seu conteúdo textual. Basta convencer alguém a experimentar um cenário de caso de uso em primeira mão.
21418 Synetech
8
Para garantir uma boa separação de conteúdo e estilo, não teremos um seletor: contains (). Portanto, em vez de estilizar uma célula com base em seu conteúdo (por exemplo, uma coluna "status" de "Abrir" ou "Resolvido"), duplicaremos o conteúdo nos atributos de exibição e de classe e, então, selecionaremos . Excelente!
Jon Kloske
122

Você precisaria adicionar um atributo de dados às linhas chamadas data-gendercom a maleou femalevalue e usar o seletor de atributos:

HTML:

<td data-gender="male">...</td>

CSS:

td[data-gender="male"] { ... }
Esteban Küber
fonte
10
Não há problema em usar atributos de dados personalizados com Javascript e CSS. Consulte MDN usando atributos de dados
Michael Benjamin
1
Eu concordo que o atributo data seja como ele deve ser tratado, MAS, uma regra CSS como td.male geralmente é muito mais fácil de configurar, especialmente com angular que pode ser algo como: <td class = "{{person.gender}} ">
DJDaveMark 4/17
É claro que o gênero é um exemplo típico em que as aulas funcionariam. Mas e se os dados forem mais variados do que apenas maleou female? O fato de classser preenchido com outras classes, a ordem delas não é garantida, pode haver colisões com outros nomes de classes, etc., torna o classlocal pior para esse tipo de coisa. Um data-*atributo dedicado isola seus dados de todas essas coisas e facilita a correspondência parcial, etc., usando seletores de atributos.
Stijn de Witt
Há outra resposta com um trecho de código funcional que usa atributos de dados de maneira semelhante.
Josh Habdas 9/01/19
65

Na verdade, existe uma base muito conceitual para o porquê disso não ter sido implementado. É uma combinação de basicamente três aspectos:

  1. O conteúdo do texto de um elemento é efetivamente filho desse elemento
  2. Você não pode segmentar diretamente o conteúdo do texto
  3. CSS não permite ascensão com seletores

Esses três juntos significam que, quando você tiver o conteúdo do texto, não poderá voltar ao elemento que o contém e não poderá estilizar o texto atual. Provavelmente, isso é significativo, pois a descida permite apenas um rastreamento singular do contexto e da análise do estilo SAX. Os seletores ascendentes ou outros envolvendo outros eixos introduzem a necessidade de soluções transversais ou similares mais complexas que complicariam bastante a aplicação de CSS ao DOM.

Matt Whipple
fonte
4
Isso é verdade. É para isso que serve o XPath. Se você puder executar o seletor em JS / jQuery ou em uma biblioteca de análise (em oposição a apenas CSS), seria possível usar a text()função XPath .
Mark Thomas
Esse é um bom argumento sobre o conteúdo . Mas como eu gostaria que você pudesse :contains(<sub-selector>)selecionar um elemento que contenha outros elementos específicos. Gostaria div:contains(div[id~="bannerAd"])de se livrar do anúncio e do seu contêiner.
Lawrence Dol
27

Você pode definir o conteúdo como atributo de dados e, em seguida, usar seletores de atributo , conforme mostrado aqui:

/* Select every cell containing word "male" */
td[data-content="male"] {
  color: red;
}

/* Select every cell starting on "p" case insensitive */
td[data-content^="p" i] {
  color: blue;
}

/* Select every cell containing "4" */
td[data-content*="4"] {
  color: green;
}
<table>
  <tr>
    <td data-content="Peter">Peter</td>
    <td data-content="male">male</td>
    <td data-content="34">34</td>
  </tr>
  <tr>
    <td data-conten="Susanne">Susanne</td>
    <td data-content="female">female</td>
    <td data-content="14">14</td>
  </tr>
</table>

Você também pode usar o jQuery para definir facilmente os atributos de conteúdo de dados:

$(function(){
  $("td").each(function(){
    var $this = $(this);
    $this.attr("data-content", $this.text());
  });
});
Buksy
fonte
3
observe que .text()e .textContentsão bastante pesados, tente evitá-los em listas longas ou em textos grandes sempre que possível. .html()e .innerHTMLsão rápidos.
oriadam 18/07/19
@JoshHabdas, você pode fazer um exemplo jsbin / jsfiddle da sua alteração?
Buksy
1
Se você for usar o jQuery, use o seletor contains:$("td:contains(male)")
huwiler
Se o conteúdo for editável pelo usuário ( contenteditable='true'- meu caso), não será suficiente definir o valor do data-contentatributo antes que a página seja renderizada. Tem que ser atualizado continuamente. Aqui o que eu uso:<td onkeypress="event.target.setAttribute('data-content', event.target.textContent);">
caram 03/04
13

Como o CSS não possui esse recurso, você precisará usar JavaScript para estilizar as células por conteúdo. Por exemplo, com XPath contains:

var elms = document.evaluate( "//td[contains(., 'male')]", node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null )

Em seguida, use o resultado da seguinte maneira:

for ( var i=0 ; i < elms.snapshotLength; i++ ){
   elms.snapshotItem(i).style.background = "pink";
}

https://jsfiddle.net/gaby_de_wilde/o7bka7Ls/9/

user40521
fonte
isto é javascript, como isso está relacionado à pergunta?
rubo77
3
Como você não pode selecionar pelo conteúdo em css, precisará usar js para fazer isso. Enquanto você está exatamente onde aponta que isso não é CSS, o uso de um atributo de dados não é selecionado por conteúdo. Nós só podemos imaginar por que o leitor quer selecionar células por conteúdo, que tipo de acesso que ela tem para o html, quantas partidas, quão grande é a tabela é etc etc
user40521
7

Receio que isso não seja possível, porque o conteúdo não é um atributo nem é acessível através de uma pseudo classe. A lista completa de seletores CSS3 pode ser encontrada na especificação CSS3 .

Edwin V.
fonte
4

Para aqueles que desejam fazer seleções de texto em CSS do Selenium, esse script pode ser útil.

O truque é selecionar o pai do elemento que você está procurando e procurar o filho que possui o texto:

public static IWebElement FindByText(this IWebDriver driver, string text)
{
    var list = driver.FindElement(By.CssSelector("#RiskAddressList"));
    var element = ((IJavaScriptExecutor)driver).ExecuteScript(string.Format(" var x = $(arguments[0]).find(\":contains('{0}')\"); return x;", text), list);
    return ((System.Collections.ObjectModel.ReadOnlyCollection<IWebElement>)element)[0];
}

Isso retornará o primeiro elemento se houver mais de um, pois sempre é um elemento, no meu caso.

Matas Vaitkevicius
fonte
Você pode usar TD:contains('male')se estiver usando o Selenium: imho só o usará se não puder atualizar a fonte para adicionar classes. por exemplo, se você estiver testando código legado ou sua empresa não permitir que você fale com os desenvolvedores (eurgh!) porque seus testes serão frágeis e serão interrompidos se alguém alterar o texto posteriormente masculineou alterar o idioma. Os documentos do SauceLabs mostram o uso containsaqui ( saucelabs.com/resources/articles/selenium-tips-css-selectors ) + cc @matas vaitkevicius Perdi alguma coisa?
snowcode 14/11/19
3

Concordo que o atributo de dados (resposta do viajante) é como ele deve ser tratado, MAS, regras de CSS como:

td.male { color: blue; }
td.female { color: pink; }

muitas vezes pode ser muito mais fácil de configurar, especialmente com bibliotecas do lado do cliente, como angularjs, que podem ser tão simples quanto:

<td class="{{person.gender}}">

Apenas certifique-se de que o conteúdo seja apenas uma palavra! Ou você pode até mapear para diferentes nomes de classes CSS com:

<td ng-class="{'masculine': person.isMale(), 'feminine': person.isFemale()}">

Para completar, eis a abordagem de atributo de dados:

<td data-gender="{{person.gender}}">
DJDaveMark
fonte
As ligações de modelo são uma ótima solução. Mas é um pouco difícil criar nomes de classe com base na estrutura de marcação (embora isso seja o que o BEM faz na prática).
Josh Habdas
2

A resposta da @ voyager sobre o uso do data-*atributo (por exemplo, data-gender="female|male"é a abordagem mais eficaz e compatível com os padrões a partir de 2017:

[data-gender='male'] {background-color: #000; color: #ccc;}

Quase todos os objetivos podem ser alcançados, pois existem alguns seletores, embora limitados, orientados ao redor do texto. A :: first-letter é um pseudo-elemento que pode aplicar um estilo limitado à primeira letra de um elemento. Há também um pseudo-elemento :: first-line , além de obviamente selecionar a primeira linha de um elemento (como um parágrafo) também implica que é óbvio que o CSS pode ser usado para estender esse recurso existente para estilizar aspectos específicos de um textNode .

Até que essa defesa seja bem - sucedida e seja implementada, a próxima melhor coisa que eu poderia sugerir, quando aplicável, é explode/ splitwords usando um delimitador de espaço, produza cada palavra individual dentro de um spanelemento e, em seguida, se o objetivo da palavra / estilo for previsível, em combinação com : nth seletores :

$p = explode(' ',$words);
foreach ($p as $key1 => $value1)
{
 echo '<span>'.$value1.'</span>;
}

Else se não previsível para, mais uma vez, a resposta uso da Voyager sobre o uso do data-*atributo. Um exemplo usando o PHP:

$p = explode(' ',$words);
foreach ($p as $key1 => $value1)
{
 echo '<span data-word="'.$value1.'">'.$value1.'</span>;
}
John
fonte
2

Se você estiver usando o Chimp / Webdriver.io , eles suportam muito mais seletores de CSS do que a especificação de CSS.

Isso, por exemplo, clicará na primeira âncora que contém as palavras "urso ruim":

browser.click("a*=Bad Bear");
Ryan Shillington
fonte
1

A maioria das respostas aqui tenta oferecer uma alternativa a como escrever o código HTML para incluir mais dados, porque pelo menos até CSS3 você não pode selecionar um elemento pelo texto interno parcial. Mas isso pode ser feito, você só precisa adicionar um pouco de JavaScript baunilha, observe que feminino também contém masculino, ele será selecionado:

      cells = document.querySelectorAll('td');
    	console.log(cells);
      [].forEach.call(cells, function (el) {
    	if(el.innerText.indexOf("male") !== -1){
    	//el.click(); click or any other option
    	console.log(el)
    	}
    });
 <table>
      <tr>
        <td>Peter</td>
        <td>male</td>
        <td>34</td>
      </tr>
      <tr>
        <td>Susanne</td>
        <td>female</td>
        <td>14</td>
      </tr>
    </table>

<table>
  <tr>
    <td data-content="Peter">Peter</td>
    <td data-content="male">male</td>
    <td data-content="34">34</td>
  </tr>
  <tr>
    <td data-conten="Susanne">Susanne</td>
    <td data-content="female">female</td>
    <td data-content="14">14</td>
  </tr>
</table>
Eduard Florinescu
fonte
1

Acho que a opção de atributo é sua melhor aposta, se você não quiser usar javascript ou jquery.

Por exemplo, para estilizar todas as células da tabela com a palavra ready, no HTML, faça o seguinte:

 <td status*="ready">Ready</td>

Em seguida, em css:

td[status*="ready"] {
        color: red;
    }
Anthony
fonte
0

Você também pode usar contentcom attr()e células de tabela de estilo que são ::not :empty

th::after { content: attr(data-value) }
td::after { content: attr(data-value) }
td[data-value]:not(:empty) {
  color: fuchsia;
}
<table>
  <tr>
    <th data-value="Peter"></th>
    <td data-value="male">&#x0200B;</td>
    <td data-value="34"></td>
  </tr>
  <tr>
    <th data-value="Susanne"></th>
    <td data-value="female"></td>
    <td data-value="12"></td>
  </tr>
  <tr>
    <th data-value="Lucas"></th>
    <td data-value="male">&#x0200B;</td>
    <td data-value="41"></td>
  </tr>
</table>

A ZeroWidthSpaceé usado para alterar a cor e pode ser adicionado usando JavaScript vanilla:

const hombres = document.querySelectorAll('td[data-value="male"]');
hombres.forEach(hombre => hombre.innerHTML = '&#x0200B;');

Embora a <td>tag s end possa ser omitida, isso pode fazer com que ela seja tratada como não vazia.

Josh Habdas
fonte
0

Se você não criar o DOM (por exemplo, em um script de usuário), poderá fazer o seguinte com JS puro:

for ( td of document.querySelectorAll('td') ) {
  console.debug("text:", td, td.innerText)
  td.setAttribute('text', td.innerText)
}
for ( td of document.querySelectorAll('td[text="male"]') )
  console.debug("male:", td, td.innerText)
<table>
  <tr>
    <td>Peter</td>
    <td>male</td>
    <td>34</td>
  </tr>
  <tr>
    <td>Susanne</td>
    <td>female</td>
    <td>12</td>
  </tr>
</table>

Saída do console

text: <td> Peter
text: <td> male
text: <td> 34
text: <td> Susanne
text: <td> female
text: <td> 12
male: <td text="male"> male
GeroldBroser restabelece Monica
fonte
-2

Fazendo pequenos widgets de filtro como este:

    var searchField = document.querySelector('HOWEVER_YOU_MAY_FIND_IT')
    var faqEntries = document.querySelectorAll('WRAPPING_ELEMENT .entry')

    searchField.addEventListener('keyup', function (evt) {
        var testValue = evt.target.value.toLocaleLowerCase();
        var regExp = RegExp(testValue);

        faqEntries.forEach(function (entry) {
            var text = entry.textContent.toLocaleLowerCase();

            entry.classList.remove('show', 'hide');

            if (regExp.test(text)) {
                entry.classList.add('show')
            } else {
                entry.classList.add('hide')
            }
        })
    })
campino2k
fonte
-2

A sintaxe desta pergunta se parece com a sintaxe do Robot Framework. Nesse caso, embora não exista um seletor de CSS que você possa usar contém, existe uma palavra-chave SeleniumLibrary que você pode usar. O elemento Esperar até que contenha .

Exemplo:

Wait Until Element Contains  | ${element} | ${contains}
Wait Until Element Contains  |  td | male
Eftychia Thomaidou
fonte