Existe-t-il un sélecteur CSS pour les éléments contenant certains textes?


512

Je recherche un sélecteur CSS pour le tableau suivant:

Peter    | male    | 34
Susanne  | female  | 12

Existe-t-il un sélecteur pour faire correspondre tous les DT contenant "masculin"?


1
Le problème est que cela serait très difficile à mettre en œuvre de manière performante.
Ms2ger

7
Un sélecteur XPath peut le faire avec la méthode .text () (si vous préférez ne pas utiliser d'exécuteur JavaScript).
djangofan

11
Voici un exemple de la façon dont vous pouvez le faire en utilisant xpath: // h1 [text () = 'Session'] et vous pouvez tester xpath dans Chrome en tapant $ x ("// h1 [text () = 'Session']" ) dans la console
VinnyG

1
Ce serait tellement pratique. Par exemple, une cellule de tableau contenant une coche ou la chaîne "Oui", "Réussite", "OK" etc. pourrait être verte.
Andreas Rejbrand

les $xréponses à la question. pour la question d'origine, $x("//td[text()='male']")fait l'affaire
Xiao

Réponses:


394

Si je lis correctement la spécification , non.

Vous pouvez faire correspondre un élément, le nom d'un attribut dans l'élément et la valeur d'un attribut nommé dans un élément. Cependant, je ne vois rien pour faire correspondre le contenu d'un élément.



34
Pour répondre à ma propre question: oui, c'est encore vrai pour aujourd'hui! Les sélecteurs de contenu ont été dépréciés! Plus de sélecteurs de contenu depuis CSS3. ( w3.org/TR/css3-selectors/#selectors )
Wlad

158

Utilisation de jQuery:

$('td:contains("male")')

7
J'ai fini par avoir l'opposé de ceci, qui est: jQuery (element) .not (": contains ('string')")
Jazzy

311
Sauf que ce n'est pas CSS, c'est JavaScript.
Synetech du

9
D'accord - c'est pourquoi ma réponse a dit que j'utilisais jQuery, pas CSS. Mais cette partie de ma réponse a été modifiée. =)
moettinger

18
mais le mot fe male lui-même contient aussi le mot 'male' non? ça marche juste parce que le mâle est le premier? Bug en attente de se produire s'ils sont réorganisés?
Michael Durrant

5
@Michael Durrant: Vous plaisantez, mais la correspondance de chaînes entre éléments (ce qui est un problème encore plus important que les correspondances de sous-chaînes régulières dans le même élément) est en fait l'une des principales raisons: contains () a été supprimé.
BoltClock

158

On dirait qu'ils y pensaient pour la spécification CSS3, mais cela n'a pas fait la coupe .

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


9
Looks like they were thinking about it for the CSS3 spec but it didn't make the cut.Et pour une bonne raison, cela violerait toute la prémisse de séparer le style, le contenu, la structure et le comportement.
Synetech

56
@Synetech Il peut réellement aider à séparer le style du contenu, car cela signifie que le contenu n'a pas besoin de connaître son client va considérer comme important de baser le style. À l'heure actuelle, notre contenu html est généralement étroitement associé au CSS en incluant des classes dont nous savons que le styleur se soucie. Ils s'orientent déjà vers laisser CSS au contenu, comme en témoignent les sélecteurs de valeur d'attribut dans CSS3.
DannyMeister

8
@Synetech et comment est-ce "comportement", exactement? Pour moi, c'est une question de présentation. Il ne fait rien - il présente certains types de contenu d'une certaine manière.
BarbaraKwarc

4
@DannyMeister, oh tu n'as pas à me convaincre. Quelques années plus tard, je me retrouve ici en essayant de rechercher exactement cette fonctionnalité, et bouleversé que non seulement elle n'existe pas, mais elle a été rejetée. 😒 eBay vient de changer son interface utilisateur et a rendu son site très difficile à utiliser. J'essaie de le corriger avec une feuille de style utilisateur, mais leur balisage ne fait pas de distinction entre les enchères et les listes BIN, donc la seule façon de mettre en évidence le nombre d'enchères dans une liste est de faire correspondre l'élément par son contenu textuel. Tout ce qu'il faut pour convaincre quelqu'un, c'est de faire l'expérience directe d'un scénario d'utilisation.
Synetech

8
Pour assurer une bonne séparation du contenu et du style, nous n'aurons pas de sélecteur: contains (). Ainsi, au lieu de styliser une cellule en fonction de son contenu (par exemple, une colonne "état" de "Ouvrir" ou "Résolu"), nous dupliquerons le contenu à la fois dans l'affichage et dans les attributs de classe, puis sélectionnerons . Excellent!
Jon Kloske

122

Vous devez ajouter un attribut de données aux lignes appelées data-genderavec une valeur maleou femaleet utiliser le sélecteur d'attribut:

HTML:

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

CSS:

td[data-gender="male"] { ... }

10
Il est parfaitement acceptable d'utiliser des attributs de données personnalisés avec Javascript et CSS. Voir MDN à l'aide d'attributs de données
Michael Benjamin

1
Je suis d'accord que l'attribut data est la façon dont il doit être géré, MAIS, une règle CSS comme td.male est souvent beaucoup plus facile à configurer, en particulier avec angular qui pourrait ressembler à: <td class = "{{person.gender}} ">
DJDaveMark

Le sexe est bien sûr un exemple typique où les classes fonctionneraient. Mais que se passe-t-il si les données sont plus variées que simplement maleou female? Le fait qu'il classsoit rempli d'autres classes, l'ordre de celles-ci n'est pas garanti, il peut y avoir des collisions avec d'autres noms de classe, etc. fait classun pire endroit pour ce genre de choses. Un data-*attribut dédié isole vos données de tout cela et facilite la correspondance partielle, etc., à l'aide de sélecteurs d'attributs.
Stijn de Witt

Il existe une autre réponse avec un extrait de code de travail qui utilise les attributs de données de manière similaire.
Josh Habdas

65

Il existe en fait une base très conceptuelle pour expliquer pourquoi cela n'a pas été mis en œuvre. Il s'agit d'une combinaison de 3 aspects fondamentaux:

  1. Le contenu textuel d'un élément est effectivement un enfant de cet élément
  2. Vous ne pouvez pas cibler directement le contenu du texte
  3. CSS ne permet pas l'ascension avec des sélecteurs

Ensemble, ces 3 signifient qu'au moment où vous avez le contenu du texte, vous ne pouvez pas remonter à l'élément contenant, et vous ne pouvez pas styliser le texte actuel. Ceci est probablement significatif car la descente ne permet qu'un suivi singulier du contexte et de l'analyse de style SAX. Des sélecteurs ascendants ou autres impliquant d'autres axes introduisent le besoin d'une traversée plus complexe ou de solutions similaires qui compliqueraient grandement l'application de CSS au DOM.


4
C'est vrai. C'est à cela que sert XPath. Si vous pouvez exécuter le sélecteur dans JS / jQuery ou une bibliothèque d'analyse (par opposition à CSS uniquement), il serait alors possible d'utiliser la text()fonction de XPath .
Mark Thomas

C'est un bon point sur le contenu . Mais comment je souhaite que vous puissiez avoir un :contains(<sub-selector>)pour sélectionner un élément qui contient d'autres éléments spécifiques. Vous div:contains(div[id~="bannerAd"])souhaitez vous débarrasser de l'annonce et de son conteneur.
Lawrence Dol

27

Vous pouvez définir le contenu comme attribut de données, puis utiliser des sélecteurs d'attribut , comme illustré ici:

/* 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>

Vous pouvez également utiliser jQuery pour définir facilement les attributs de contenu de données:

$(function(){
  $("td").each(function(){
    var $this = $(this);
    $this.attr("data-content", $this.text());
  });
});

3
notez cela .text()et .textContentsont assez lourds, essayez de les éviter dans les longues listes ou les gros textes lorsque cela est possible. .html()et .innerHTMLsont rapides.
oriadam

@JoshHabdas pouvez-vous faire un exemple jsbin / jsfiddle de votre changement?
Buksy

1
Si vous allez utiliser jQuery, utilisez le sélecteur contient:$("td:contains(male)")
huwiler

Si le contenu est modifiable par l'utilisateur ( contenteditable='true'- mon cas), il ne suffit pas de définir la valeur de l' data-contentattribut avant le rendu de la page. Il doit être mis à jour en permanence. Voici ce que j'utilise:<td onkeypress="event.target.setAttribute('data-content', event.target.textContent);">
caram

13

Comme CSS ne dispose pas de cette fonctionnalité, vous devrez utiliser JavaScript pour styliser les cellules en fonction du contenu. Par exemple avec XPath contains:

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

Ensuite, utilisez le résultat comme ceci:

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

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


c'est du javascript, comment est-ce lié à la question?
rubo77

3
Comme vous ne pouvez pas sélectionner par contenu en CSS, vous devrez utiliser js pour ce faire. Bien que vous ayez raison de signaler qu'il ne s'agit pas de CSS, l'utilisation d'un attribut de données ne sélectionne pas par contenu. Nous pouvons seulement deviner pourquoi le lecteur veut sélectionner des cellules par contenu, quel type d'accès il a au html, combien de correspondances, quelle est la taille de la table, etc. etc.
user40521

7

J'ai bien peur que ce ne soit pas possible, car le contenu n'est pas un attribut ni accessible via une pseudo classe. La liste complète des sélecteurs CSS3 se trouve dans la spécification CSS3 .


4

Pour ceux qui cherchent à faire des sélections de texte CSS Selenium, ce script pourrait être d'une certaine utilité.

L'astuce consiste à sélectionner le parent de l'élément que vous recherchez, puis à rechercher l'enfant qui a le texte:

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];
}

Cela retournera le premier élément s'il y en a plus d'un puisque c'est toujours un élément, dans mon cas.


Vous pouvez l'utiliser TD:contains('male')si vous utilisez Selenium: à mon humble avis, ne l'utilisez que si vous ne pouvez pas mettre à jour la source pour ajouter des classes. Par exemple, si vous testez un code hérité ou que votre entreprise ne vous laissera pas parler aux développeurs (eurgh!) car vos tests seront fragiles et se briseront si quelqu'un change le texte plus tard masculineou change de langue. La documentation de SauceLabs montre comment l'utiliser containsici ( saucelabs.com/resources/articles/selenium-tips-css-selectors ) + cc @matas vaitkevicius ai-je oublié quelque chose?
snowcode

3

Je suis d'accord que l'attribut de données (réponse du voyageur) est la façon dont il doit être géré, MAIS, les règles CSS comme:

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

peut souvent être beaucoup plus facile à configurer, en particulier avec les bibliothèques côté client comme angularjs qui peuvent être aussi simples que:

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

Assurez-vous simplement que le contenu n'est qu'un mot! Ou vous pouvez même mapper vers différents noms de classe CSS avec:

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

Pour être complet, voici l'approche des attributs de données:

<td data-gender="{{person.gender}}">

Les liaisons de modèles sont une excellente solution. Mais il semble un peu difficile de créer des noms de classe basés sur la structure de balisage (bien que ce soit à peu près ce que BEM fait dans la pratique).
Josh Habdas

2

@ voyager's answer about using data-*attribute (par exemple, data-gender="female|male"c'est l' approche la plus efficace et conforme aux normes en 2017:

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

La plupart des objectifs peuvent être atteints car il existe des sélecteurs, quoique limités, orientés autour du texte. La :: première lettre est un pseudo-élément qui peut appliquer un style limité à la première lettre d'un élément. Il y a aussi un pseudo-élément :: première ligne en plus de sélectionner évidemment la première ligne d'un élément (comme un paragraphe) implique également qu'il est évident que CSS pourrait être utilisé pour étendre cette capacité existante pour styliser des aspects spécifiques d'un textNode .

Jusqu'à ce qu'un tel plaidoyer réussisse et soit mis en œuvre, la prochaine meilleure chose que je pourrais suggérer le cas échéant est de explode/ splitmots en utilisant un suppresseur d'espace, sortir chaque mot individuel à l'intérieur d'un spanélément et puis si l'objectif de mot / style est prévisible, utiliser en combinaison avec : nth selectors :

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

Sinon, si ce n'est pas prévisible , encore une fois, utilisez la réponse de Voyager concernant l'utilisation de l' data-*attribut. Un exemple utilisant PHP:

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


1

La plupart des réponses ici tentent d'offrir une alternative à la façon d'écrire le code HTML pour inclure plus de données, car au moins jusqu'à CSS3, vous ne pouvez pas sélectionner un élément par du texte interne partiel. Mais cela peut être fait, il vous suffit d'ajouter un peu de JavaScript vanille, notez que puisque la femelle contient également le mâle, il sera sélectionné:

      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>

1

Je trouve que l'option d'attribut est votre meilleur choix si vous ne souhaitez pas utiliser javascript ou jquery.

Par exemple, pour styliser toutes les cellules du tableau avec le mot prêt, en HTML, procédez comme suit:

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

Puis en css:

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

0

Vous pouvez également utiliser contentavec attr()et styliser des cellules de tableau qui sont ::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 ZeroWidthSpaceest utilisé pour changer la couleur et peut être ajouté à l'aide de JavaScript vanille:

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

Bien que la <td>balise de fin s puisse être omise , cela peut la traiter comme non vide.


0

Si vous ne créez pas le DOM vous-même (par exemple dans un script utilisateur), vous pouvez faire ce qui suit avec du JS pur:

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>

Sortie console

text: <td> Peter
text: <td> male
text: <td> 34
text: <td> Susanne
text: <td> female
text: <td> 12
male: <td text="male"> male

-2

Faire de petits widgets de filtre comme ceci:

    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')
            }
        })
    })

-2

La syntaxe de cette question ressemble à la syntaxe de Robot Framework. Dans ce cas, bien qu'il n'y ait pas de sélecteur css que vous puissiez utiliser pour contient, il existe un mot clé SeleniumLibrary que vous pouvez utiliser à la place. L' élément Wait Until contient .

Exemple:

Wait Until Element Contains  | ${element} | ${contains}
Wait Until Element Contains  |  td | male
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.