Czy istnieje selektor CSS dla elementów zawierających określony tekst?

510

Szukam selektora CSS dla poniższej tabeli:

Peter    | male    | 34
Susanne  | female  | 12

Czy jest jakiś selektor pasujący do wszystkich niszczycieli czołgów zawierających „mężczyzna”?

jantimon
źródło
1
Problem polega na tym, że byłoby to bardzo trudne do wdrożenia w wydajny sposób.
Ms2ger,
7
Selektor XPath może to zrobić za pomocą metody .text () (jeśli wolisz nie używać executora JavaScript).
djangofan
11
Oto przykład, w jaki sposób możesz to zrobić za pomocą xpath: // h1 [text () = 'Session'] i możesz przetestować xpath w Chrome, wpisując $ x ("// h1 [text () = 'Session']” ) w konsoli
VinnyG
1
To by było takie wygodne. Na przykład komórka tabeli zawierająca znacznik wyboru lub ciąg „Tak”, „Przekaż”, „OK” itp. Może być zielona.
Andreas Rejbrand
$xodpowiada na pytanie. w przypadku pierwotnego pytania, $x("//td[text()='male']")czy podstęp
Xiao

Odpowiedzi:

392

Jeśli poprawnie odczytam specyfikację , nie.

Możesz dopasować do elementu, nazwy atrybutu w elemencie i wartości nazwanego atrybutu w elemencie. Nie widzę jednak nic dla pasujących treści w elemencie.

Dziekan J.
źródło
226
Jest jeszcze jedna sprawa krawędź: :empty.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功 法轮功
34
Aby odpowiedzieć na moje pytanie: Tak, nadal jest to prawdą na dziś! Selektory treści były przestarzałe! Żadnych selektorów treści od CSS3. ( w3.org/TR/css3-selectors/#selectors )
Wlad
158

Korzystanie z jQuery:

$('td:contains("male")')
moettinger
źródło
7
Skończyło się na tym, że potrzebuję czegoś przeciwnego, czyli: jQuery (element) .not (": zawiera ('string')")
Jazzy
309
Tyle że nie jest to CSS, czyli JavaScript.
Synetech,
9
Zgadzam się - dlatego moja odpowiedź mówi, że korzystam z jQuery, a nie CSS. Ale ta część mojej odpowiedzi została zredagowana. =)
moettinger
17
ale samo słowo fe male również zawiera słowo „male” no? czy to działa, bo mężczyzna jest pierwszy? Błąd czeka na wystąpienie, jeśli zostaną ponownie zamówione?
Michael Durrant
5
@Michael Durrant: Jesteś żartem, ale dopasowanie ciągów między elementami (co jest jeszcze większym problemem niż zwykłe dopasowania podłańcuchów w tym samym elemencie) jest w rzeczywistości jednym z głównych powodów: porzucono zawiera ().
BoltClock
158

Wygląda na to, że zastanawiali się nad specyfikacją CSS3, ale nie udało się to .

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

Jeff Beaman
źródło
9
Looks like they were thinking about it for the CSS3 spec but it didn't make the cut.I nie bez powodu naruszałoby to całą zasadę oddzielania stylu, treści, struktury i zachowania.
Synetech,
56
@ Synetech Może to rzeczywiście pomóc w oddzieleniu stylizacji od treści, ponieważ oznacza to, że treść, która nie musi wiedzieć o kliencie, będzie uważana za ważną, na której opiera się stylizacja. Obecnie nasza zawartość HTML jest zazwyczaj ściśle powiązana z css, włączając w to klasy, o których wiemy, że dba o stylizację. Już teraz przesuwają się w kierunku pozwalania CSS na treść, o czym świadczą selektory wartości atrybutów w CSS3.
DannyMeister
8
@ Synetech i jak to dokładnie jest „zachowanie”? Dla mnie to kwestia prezentacji. Nic nie robi - w określony sposób przedstawia określone typy treści.
BarbaraKwarc
4
@DannyMeister, och, nie musisz mnie przekonywać. Kilka lat później znalazłem się z powrotem tutaj, próbując poszukać dokładnie tej funkcjonalności, i denerwuję się, że nie tylko ona nie istnieje, ale została odrzucona. 😒 eBay właśnie zmienił interfejs użytkownika i bardzo utrudnił korzystanie z witryny. Próbuję to naprawić za pomocą arkusza stylów użytkownika, ale ich znaczniki nie rozróżniają aukcji i list BIN, więc jedynym sposobem na podkreślenie liczby ofert na liście jest dopasowanie elementu do jego treści tekstowej. Wystarczy, aby przekonać kogoś, aby doświadczył scenariusza użycia z pierwszej ręki.
Synetech
8
Aby zapewnić dobre rozdzielenie treści i stylu, nie będziemy mieli selektora: zawiera (). Zamiast więc stylizować komórkę w oparciu o jej zawartość (np. Kolumnę „status” „Otwarte” lub „Rozwiązane”), powielimy zawartość zarówno w atrybutach display, jak i class, a następnie wybierzemy . Świetny!
Jon Kloske
122

Trzeba dodać atrybut danych do wierszy wywoływanych data-genderza pomocą wartości malelub lub femaleużyć selektora atrybutów:

HTML:

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

CSS:

td[data-gender="male"] { ... }
Esteban Küber
źródło
10
Używanie niestandardowych atrybutów danych w JavaScript i CSS jest całkowicie w porządku. Zobacz MDN przy użyciu atrybutów danych
Michael Benjamin
1
Zgadzam się, że atrybutem danych jest sposób, w jaki powinien być obsługiwany, ALE, reguła CSS, taka jak td.male, jest często znacznie łatwiejsza do skonfigurowania, szczególnie w przypadku kątów, które mogą wyglądać mniej więcej tak: <td class = "{{person.gender}} ">
DJDaveMark,
Płeć jest oczywiście typowym przykładem, w którym działają klasy. Ale co, jeśli dane są bardziej zróżnicowane niż tylko malelub female? Fakt, że classsą wypełnione innymi klasami, ich kolejność nie jest gwarantowana, mogą wystąpić kolizje z innymi nazwami klas itp., Co jest classgorszym miejscem dla tego rodzaju rzeczy. Dedykowany data-*atrybut izoluje dane od wszystkich tych rzeczy i ułatwia wykonanie częściowego dopasowywania itp. Za pomocą selektorów atrybutów.
Stijn de Witt
Jest jeszcze jedna odpowiedź z działającym fragmentem kodu, który wykorzystuje atrybuty danych w podobny sposób.
Josh Habdas
65

Istnieje właściwie bardzo koncepcyjna podstawa, dla której nie zostało to wdrożone. Jest to połączenie zasadniczo 3 aspektów:

  1. Treść tekstowa elementu jest faktycznie dzieckiem tego elementu
  2. Nie można kierować bezpośrednio na treść tekstu
  3. CSS nie pozwala na wstąpienie do selektorów

Te 3 razem oznaczają, że do czasu, gdy masz treść tekstową, nie możesz wrócić do elementu zawierającego i nie możesz stylizować obecnego tekstu. Jest to prawdopodobnie znaczące, ponieważ zejście pozwala jedynie na pojedyncze śledzenie parsowania kontekstu i stylu SAX. Rosnące lub inne selektory obejmujące inne osie wprowadzają potrzebę bardziej złożonych rozwiązań przejścia lub podobnych rozwiązań, które znacznie skomplikowałyby zastosowanie CSS w DOM.

Matt Whipple
źródło
4
To prawda. Po to jest XPath. Jeśli możesz wykonać selektor w JS / jQuery lub bibliotece parsującej (w przeciwieństwie do CSS), wówczas możliwe byłoby użycie text()funkcji XPath .
Mark Thomas
To dobra uwaga na temat treści . Ale jak chciałbym, abyś mógł :contains(<sub-selector>)wybrać elementy, które zawierają inne określone elementy. Lubię div:contains(div[id~="bannerAd"])pozbyć się reklamy i jej pojemnika.
Lawrence Dol
27

Możesz ustawić zawartość jako atrybut danych, a następnie użyć selektorów atrybutów , jak pokazano tutaj:

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

Możesz także użyć jQuery, aby łatwo ustawić atrybuty danych:

$(function(){
  $("td").each(function(){
    var $this = $(this);
    $this.attr("data-content", $this.text());
  });
});
Buksy
źródło
3
Zauważ, że .text()i .textContentsą dość ciężkie, staraj się unikać ich na długich listach lub dużych tekstach, jeśli to możliwe. .html()i .innerHTMLsą szybkie.
oriadam
@JoshHabdas, czy możesz zrobić przykład zmiany jsbin / jsfiddle?
Buksy
1
Jeśli zamierzasz korzystać z jQuery, użyj selektora zawiera:$("td:contains(male)")
huwiler
Jeśli treść jest edytowalna przez użytkownika ( contenteditable='true'- mój przypadek), nie wystarczy ustawić wartość data-contentatrybutu przed renderowaniem strony. Musi być stale aktualizowany. Oto, czego używam:<td onkeypress="event.target.setAttribute('data-content', event.target.textContent);">
caram
13

Ponieważ CSS nie ma tej funkcji, będziesz musiał użyć JavaScript do stylizowania komórek według zawartości. Na przykład z XPath contains:

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

Następnie użyj wyniku w następujący sposób:

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

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

użytkownik40521
źródło
to jest javascript, jak to się ma do pytania?
rubo77,
3
Gdy nie możesz wybrać zawartości w css, będziesz musiał do tego użyć js. Chociaż masz rację, gdy wskazujesz, że to nie jest css, użycie atrybutu danych nie jest wybierane według zawartości. Możemy tylko zgadywać, dlaczego czytelnik chce wybrać komórki według zawartości, jaki ma dostęp do html, ile dopasowań, jak duży jest stół itp.
użytkownik40521
7

Obawiam się, że nie jest to możliwe, ponieważ treść nie jest atrybutem ani nie jest dostępna za pośrednictwem pseudoklasy. Pełna lista selektorów CSS3 znajduje się w specyfikacji CSS3 .

Edwin V.
źródło
4

Dla tych, którzy chcą wybierać tekst Selenium CSS, ten skrypt może się przydać.

Sztuką jest wybranie elementu nadrzędnego poszukiwanego elementu, a następnie wyszukanie elementu podrzędnego zawierającego tekst:

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

Zwróci pierwszy element, jeśli jest więcej niż jeden, ponieważ w moim przypadku zawsze jest to jeden element.

Matas Vaitkevicius
źródło
Możesz użyć, TD:contains('male')jeśli używasz Selenium: imho użyj go tylko wtedy, gdy nie możesz zaktualizować źródła, aby dodać klasy. np. jeśli testujesz starszy kod lub Twoja firma nie pozwoli Ci porozmawiać z deweloperami (eurgh!), ponieważ twoje testy będą kruche i zepsują się, jeśli ktoś zmieni tekst później masculinelub zmieni język. Dokumenty SauceLabs pokazują użycie containstutaj ( saucelabs.com/resources/articles/selenium-tips-css-selectors ) + cc @matas vaitkevicius, czy coś przeoczyłem?
kod śniegu
3

Zgadzam się, że atrybut danych (odpowiedź podróżnika) jest taki, jak powinien być traktowany, ALE, reguły CSS, takie jak:

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

często może być znacznie łatwiejsze do skonfigurowania, szczególnie w bibliotekach po stronie klienta, takich jak angularjs, które mogą być tak proste, jak:

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

Upewnij się, że treść jest tylko jednym słowem! Lub możesz nawet mapować na różne nazwy klas CSS za pomocą:

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

Dla kompletności, oto podejście do atrybutu danych:

<td data-gender="{{person.gender}}">
DJDaveMark
źródło
Wiązania szablonów są doskonałym rozwiązaniem. Jednak tworzenie nazw klas na podstawie struktury znaczników jest nieco kłujące (choć w praktyce tak robi BEM ).
Josh Habdas
2

Odpowiedź @ voyagera na temat używania data-*atrybutu (np. data-gender="female|male"jest najbardziej skutecznym i zgodnym ze standardami podejściem od 2017 r .:

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

Niemal większość celów można osiągnąć, ponieważ istnieją pewne, choć ograniczone selektory zorientowane na tekst. :: first-letter jest pseudo-element , który można zastosować ograniczoną stylizacji do pierwszej litery elementu. Istnieje również :: pierwsza linia pseudoelement poza oczywistym wybraniem pierwszego wiersza elementu (takiego jak akapit), sugeruje również, że jest oczywiste, że CSS można wykorzystać do rozszerzenia tej istniejącej możliwości na stylizowanie określonych aspektów textNode .

Dopóki takie poparcie nie odniesie sukcesu i nie zostanie wdrożone, następną najlepszą rzeczą, którą mogę zasugerować, gdy ma to zastosowanie, jest explode/ splitsłowa przy użyciu separatora spacji, wypisz każde pojedyncze słowo wewnątrz spanelementu, a następnie, jeśli cel słowa / stylu jest przewidywalny, użyj w połączeniu z : n-tymi selektorami :

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

W przeciwnym razie skorzystaj z odpowiedzi podróżnika na temat użycia data-*atrybutu. Przykład użycia PHP:

$p = explode(' ',$words);
foreach ($p as $key1 => $value1)
{
 echo '<span data-word="'.$value1.'">'.$value1.'</span>;
}
Jan
źródło
1

Większość odpowiedzi tutaj stanowi alternatywę dla pisania kodu HTML w celu uwzględnienia większej ilości danych, ponieważ przynajmniej do CSS3 nie można wybrać elementu przez częściowy tekst wewnętrzny. Ale można to zrobić, wystarczy dodać trochę waniliowego JavaScript, zauważ, ponieważ kobieta zawiera również mężczyznę, zostanie wybrana:

      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
źródło
1

Opcja atrybutu jest dla mnie najlepszym rozwiązaniem, jeśli nie chcesz używać javascript ani jquery.

Np. Aby stylizować wszystkie komórki tabeli słowem „gotowe”, w HTML wykonaj następujące czynności:

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

Następnie w css:

td[status*="ready"] {
        color: red;
    }
Anthony
źródło
0

Możesz również używać contentz attr()komórkami tabeli i stylami, które są ::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 ZeroWidthSpacesłuży do zmiany koloru i może być dodany za pomocą waniliowego JavaScript:

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

Chociaż <td>znacznik końca s może zostać pominięty , może to spowodować, że będzie traktowane jako niepuste.

Josh Habdas
źródło
0

Jeśli nie utworzysz DOM samodzielnie (np. W skrypcie użytkownika), możesz wykonać następujące czynności z czystym JS:

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>

Wyjście konsoli

text: <td> Peter
text: <td> male
text: <td> 34
text: <td> Susanne
text: <td> female
text: <td> 12
male: <td text="male"> male
GeroldBroser przywraca Monikę
źródło
-2

Robienie takich małych widgetów filtrujących:

    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
źródło
-2

Składnia tego pytania wygląda jak składnia Robot Framework. W takim przypadku, chociaż nie ma selektora css, którego można użyć, zawiera słowo kluczowe SeleniumLibrary, którego można użyć zamiast tego. Element Wait Until zawiera .

Przykład:

Wait Until Element Contains  | ${element} | ${contains}
Wait Until Element Contains  |  td | male
Eftychia Thomaidou
źródło