Gibt es eine CSS-Auswahl für Elemente, die bestimmten Text enthalten?


512

Ich suche einen CSS-Selektor für die folgende Tabelle:

Peter    | male    | 34
Susanne  | female  | 12

Gibt es einen Selektor für alle TDs, die "männlich" enthalten?


1
Das Problem ist, dass dies sehr schwer performant zu implementieren wäre.
Ms2ger

7
Ein XPath-Selektor kann dies mit der Methode .text () tun (wenn Sie keinen JavaScript-Executor verwenden möchten).
Djangofan

11
Hier ist ein Beispiel, wie Sie dies mit xpath tun können: // h1 [text () = 'Session'] und Sie können xpath in Chrome testen, indem Sie $ x ("// h1 [text () = 'Session']" eingeben. ) in der Konsole
VinnyG

1
Das wäre so bequem. Beispielsweise könnte eine Tabellenzelle mit einem Häkchen oder der Zeichenfolge "Ja", "Bestanden", "OK" usw. grün sein.
Andreas Rejbrand

das $xbeantwortet die frage. für die ursprüngliche Frage $x("//td[text()='male']")macht der Trick
Xiao

Antworten:


394

Wenn ich die Spezifikation richtig gelesen habe , nein.

Sie können ein Element, den Namen eines Attributs im Element und den Wert eines benannten Attributs in einem Element abgleichen. Ich sehe jedoch nichts, um Inhalte innerhalb eines Elements abzugleichen.


228
Es gibt einen Randfall : :empty.
Ciro Santilli 6 冠状 病. 事件 6

34
Um meine eigene Frage zu beantworten: Ja, das gilt noch heute! Inhaltsselektoren waren veraltet! Keine Inhaltsselektoren mehr seit CSS3. ( w3.org/TR/css3-selectors/#selectors )
Wlad

158

Verwenden von jQuery:

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

7
Ich brauchte das Gegenteil davon, nämlich: jQuery (Element) .not (": enthält ('string')")
Jazzy

311
Nur dass dies kein CSS ist, sondern JavaScript.
Synetech

9
Einverstanden - deshalb lautete meine Antwort, dass ich jQuery und nicht CSS verwende. Aber dieser Teil meiner Antwort wurde herausgeschnitten. =)
Moettinger

18
aber das wort fe male selbst enthält auch das wort 'male' nein? funktioniert es nur, weil männlich zuerst ist? Fehler, der darauf wartet, passiert zu werden, wenn sie nachbestellt werden?
Michael Durrant

5
@ Michael Durrant: Sie scherzen, aber der elementübergreifende String-Abgleich (der ein noch größeres Problem darstellt als normale Teilstring-Übereinstimmungen innerhalb desselben Elements) ist in der Tat einer der Hauptgründe: enthält () wurde gelöscht.
BoltClock

158

Sieht so aus, als hätten sie für die CSS3-Spezifikation darüber nachgedacht, aber es hat den Schnitt nicht geschafft .

:contains()CSS3-Selektor 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.Und aus gutem Grund würde dies die gesamte Prämisse der Trennung von Stil, Inhalt, Struktur und Verhalten verletzen.
Synetech

56
@Synetech Es kann tatsächlich helfen, das Styling vom Inhalt zu trennen, da es bedeutet, dass der Inhalt nicht über seinen Client Bescheid wissen muss, was als wichtig für die Basis des Stylings angesehen wird. Momentan ist unser HTML-Inhalt normalerweise eng mit dem CSS verbunden, indem Klassen eingeschlossen werden, von denen wir wissen, dass sie den Styler interessieren. Sie verlagern sich bereits darauf, CSS am Inhalt zuzulassen, wie die Attributwertselektoren in CSS3 belegen.
DannyMeister

8
@Synetech und wie ist es genau "Verhalten"? Für mich ist es eine Frage der Präsentation. Es ist nicht tut nichts - es stellt bestimmte Arten von Inhalten in einer bestimmten Art und Weise.
BarbaraKwarc

4
@DannyMeister, oh du musst mich nicht überzeugen. Ein paar Jahre später bin ich wieder hier, während ich versuche, genau nach dieser Funktionalität zu suchen, und bin verärgert darüber, dass sie nicht nur nicht existiert, sondern auch abgelehnt wurde. 😒 eBay hat gerade die Benutzeroberfläche geändert und die Nutzung der Website sehr schwierig gemacht. Ich versuche, das Problem mit einem Benutzer-Stylesheet zu beheben, aber das Markup unterscheidet nicht zwischen Auktionen und BIN-Listen. Die einzige Möglichkeit, die Anzahl der Gebote in einer Liste hervorzuheben, besteht darin, das Element anhand seines Textinhalts abzugleichen. Um jemanden zu überzeugen, muss er lediglich ein Anwendungsszenario aus erster Hand erleben.
Synetech

8
Um eine gute Trennung von Inhalt und Stil zu gewährleisten, verfügen wir nicht über einen Selector: includes (). Anstatt eine Zelle basierend auf ihrem Inhalt zu gestalten (z. B. eine "Status" -Spalte von "Öffnen" oder "Gelöst"), duplizieren wir den Inhalt sowohl in der Anzeige als auch in den Klassenattributen und wählen ihn dann aus . Ausgezeichnet!
Jon Kloske

122

Sie müssten den data-gendermit a maleoder femalevalue aufgerufenen Zeilen ein Datenattribut hinzufügen und den Attributselektor verwenden:

HTML:

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

CSS:

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

10
Es ist vollkommen in Ordnung, benutzerdefinierte Datenattribute mit Javascript und CSS zu verwenden. Siehe MDN Verwenden von Datenattributen
Michael Benjamin

1
Ich bin damit einverstanden, dass das Datenattribut so behandelt wird, wie es behandelt werden soll, ABER eine CSS-Regel wie td.male ist oft viel einfacher einzurichten, insbesondere bei Winkeln, die ungefähr so ​​aussehen könnten: <td class = "{{person.gender}} ">
DJDaveMark

Das Geschlecht ist natürlich ein typisches Beispiel dafür, wo Klassen funktionieren würden. Was aber, wenn die Daten vielfältiger sind als nur maleoder female? Die Tatsache, dass classmit anderen Klassen gefüllt ist, deren Reihenfolge nicht garantiert ist, es kann zu Kollisionen mit anderen Klassennamen usw. kommen, macht classeinen schlechteren Ort für diese Art von Sachen. Ein dediziertes data-*Attribut isoliert Ihre Daten von all dem Zeug und erleichtert das teilweise Abgleichen usw. mithilfe von Attributselektoren.
Stijn de Witt

Es gibt eine andere Antwort mit einem Arbeitscode-Snippet, das Datenattribute auf ähnliche Weise verwendet.
Josh Habdas

65

Es gibt tatsächlich eine sehr konzeptionelle Grundlage dafür, warum dies nicht implementiert wurde. Es ist eine Kombination von im Grunde 3 Aspekten:

  1. Der Textinhalt eines Elements ist effektiv ein untergeordnetes Element dieses Elements
  2. Sie können den Textinhalt nicht direkt ausrichten
  3. CSS erlaubt keinen Aufstieg mit Selektoren

Diese 3 zusammen bedeuten, dass Sie zu dem Zeitpunkt, an dem Sie den Textinhalt haben, nicht mehr zum enthaltenen Element aufsteigen und den vorliegenden Text nicht formatieren können. Dies ist wahrscheinlich von Bedeutung, da der Abstieg nur eine singuläre Verfolgung des Kontexts und der Analyse im SAX-Stil ermöglicht. Aufsteigende oder andere Selektoren, an denen andere Achsen beteiligt sind, erfordern komplexere Durchquerungs- oder ähnliche Lösungen, die die Anwendung von CSS auf das DOM erheblich erschweren würden.


4
Das ist wahr. Dafür ist XPath da. Wenn Sie den Selektor in JS / jQuery oder einer Parsing-Bibliothek ausführen können (im Gegensatz zu nur CSS), können Sie die XPath- text()Funktion verwenden.
Mark Thomas

Das ist ein guter Punkt zum Inhalt . Aber wie ich wünschte, Sie könnten :contains(<sub-selector>)ein Element auswählen, das andere spezifische Elemente enthält. Wie div:contains(div[id~="bannerAd"])auf der Anzeige loszuwerden und es des Containers.
Lawrence Dol

27

Sie können den Inhalt als Datenattribut festlegen und dann Attributselektoren verwenden , wie hier gezeigt:

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

Sie können auch jQuery verwenden, um die Dateninhaltsattribute einfach festzulegen:

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

3
Beachten Sie, dass .text()und .textContentsind ziemlich schwer, versuchen Sie, sie in langen Listen oder großen Texten zu vermeiden, wenn möglich. .html()und .innerHTMLsind schnell.
Oriadam

@JoshHabdas Kannst du ein jsbin / jsfiddle-Beispiel für deine Änderung machen?
Buksy

1
Wenn Sie jQuery verwenden $("td:contains(male)")
möchten

Wenn der Inhalt vom Benutzer bearbeitet werden kann ( contenteditable='true'- in meinem Fall), reicht es nicht aus, den Wert des data-contentAttributs festzulegen, bevor die Seite gerendert wird. Es muss ständig aktualisiert werden. Hier was ich benutze:<td onkeypress="event.target.setAttribute('data-content', event.target.textContent);">
Karam

13

Da CSS diese Funktion nicht bietet, müssen Sie JavaScript verwenden, um Zellen nach Inhalt zu formatieren. Zum Beispiel mit XPaths contains:

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

Verwenden Sie dann das Ergebnis wie folgt:

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

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


Dies ist Javascript. Wie hängt das mit der Frage zusammen?
rubo77

3
Da Sie nicht nach Inhalt in CSS auswählen können, müssen Sie dazu js verwenden. Während Sie genau dort sind, wo Sie darauf hinweisen, dass dies kein CSS ist, wird die Verwendung eines Datenattributs nicht nach Inhalt ausgewählt. Wir können nur raten, warum der Leser Zellen nach Inhalt auswählen möchte, welche Art von Zugriff sie auf das HTML hat, wie viele Übereinstimmungen, wie groß die Tabelle ist usw. usw.
user40521

7

Ich fürchte, dies ist nicht möglich, da der Inhalt weder ein Attribut ist noch über eine Pseudoklasse zugänglich ist. Die vollständige Liste der CSS3-Selektoren finden Sie in der CSS3-Spezifikation .


4

Für diejenigen, die Selenium CSS-Text auswählen möchten, kann dieses Skript von Nutzen sein.

Der Trick besteht darin, das übergeordnete Element des gesuchten Elements auszuwählen und dann nach dem untergeordneten Element zu suchen, das den Text enthält:

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

Dies gibt das erste Element zurück, wenn es mehr als ein Element gibt, da es in meinem Fall immer ein Element ist.


Sie können es verwenden, TD:contains('male')wenn Sie Selenium verwenden: imho verwenden Sie es nur, wenn Sie die Quelle nicht aktualisieren können, um Klassen hinzuzufügen. zB wenn Sie Legacy-Code testen oder Ihr Unternehmen Sie nicht mit den Entwicklern sprechen lässt (eurgh!), weil Ihre Tests brüchig sind und brechen, wenn jemand den Text später in masculineoder in die Sprache ändert. SauceLabs docs zeigt die Verwendung von containshier ( saucelabs.com/resources/articles/selenium-tips-css-selectors ) + cc @matas vaitkevicius habe ich etwas verpasst?
Snowcode

3

Ich bin damit einverstanden, dass das Datenattribut (die Antwort des Reisenden) lautet, wie es behandelt werden soll, ABER CSS-Regeln wie:

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

kann oft viel einfacher einzurichten sein, insbesondere bei clientseitigen Bibliotheken wie anglejs, die so einfach sein können wie:

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

Stellen Sie einfach sicher, dass der Inhalt nur ein Wort ist! Oder Sie können sogar verschiedenen CSS-Klassennamen zuordnen mit:

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

Der Vollständigkeit halber ist hier der Datenattributansatz:

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

Vorlagenbindungen sind eine großartige Lösung. Es fühlt sich jedoch etwas schwierig an, Klassennamen basierend auf der Markup-Struktur zu erstellen (obwohl BEM dies in der Praxis ziemlich genau tut).
Josh Habdas

2

Antwort von @ voyager zur Verwendung von data-*Attributen (z. B. data-gender="female|male"der effektivste und standardkonformste Ansatz ab 2017:

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

Die meisten Ziele können erreicht werden, da es einige, wenn auch begrenzte Selektoren gibt, die sich an Text orientieren. Der :: first-letter ist ein Pseudo-Element , die begrenzte Styling mit dem ersten Buchstaben eines Elements anwenden kann. Es gibt auch ein :: First-Line -Pseudoelement, neben der offensichtlichen Auswahl der ersten Zeile eines Elements (z. B. eines Absatzes) impliziert dies auch, dass es offensichtlich ist, dass CSS verwendet werden könnte, um diese vorhandene Fähigkeit zu erweitern, um bestimmte Aspekte eines textNode zu formatieren .

Bis zur Befürwortung erfolgreich und ist die nächste beste Sache umgesetzt ich vorschlagen könnte , wenn anwendbar ist explode/ splitWorte einen Raum deliminator, Ausgang jedes einzelne Wort im Innern eines mit spanElemente und dann , wenn das Wort / Ziel Styling ist vorhersehbar Verwendung in Kombination mit : n Selektoren ::

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

Andernfalls, wenn dies nicht vorhersehbar ist , verwenden Sie erneut die Antwort des Voyagers zur Verwendung von data-*Attributen. Ein Beispiel mit PHP:

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


1

Die meisten Antworten hier versuchen, eine Alternative zum Schreiben des HTML-Codes anzubieten, um mehr Daten aufzunehmen, da Sie zumindest bis zu CSS3 kein Element durch partiellen inneren Text auswählen können. Aber es kann getan werden, Sie müssen nur ein bisschen Vanille-JavaScript hinzufügen, beachten Sie, da weiblich auch männlich enthält, wird es ausgewählt:

      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

Ich finde die Attributoption die beste Wahl, wenn Sie kein Javascript oder keine Abfrage verwenden möchten.

Um beispielsweise alle Tabellenzellen mit dem Wort bereit zu formatieren, gehen Sie in HTML folgendermaßen vor:

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

Dann in CSS:

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

0

Sie können auch verwenden , contentmit attr()und Stil Tabellenzellen , die sind ::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 ZeroWidthSpacewird zum Ändern der Farbe verwendet und kann mit Vanille-JavaScript hinzugefügt werden:

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

Obwohl das <td>s-End-Tag möglicherweise weggelassen wird , kann dies dazu führen, dass es als nicht leer behandelt wird.


0

Wenn Sie das DOM nicht selbst erstellen (z. B. in einem Userscript), können Sie mit reinem JS Folgendes tun:

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>

Konsolenausgabe

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

-2

Kleine Filter-Widgets wie folgt ausführen:

    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

Die Syntax dieser Frage ähnelt der Robot Framework-Syntax. In diesem Fall gibt es, obwohl es keinen CSS-Selektor gibt, für den Sie enthalten können, ein SeleniumLibrary-Schlüsselwort, das Sie stattdessen verwenden können. Das Element Warten bis enthält .

Beispiel:

Wait Until Element Contains  | ${element} | ${contains}
Wait Until Element Contains  |  td | male
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.