Apakah ada pemilih CSS untuk elemen yang mengandung teks tertentu?

512

Saya mencari pemilih CSS untuk tabel berikut:

Peter    | male    | 34
Susanne  | female  | 12

Apakah ada pemilih yang cocok dengan semua TD yang mengandung "laki-laki"?

Jimimon
sumber
1
Masalahnya adalah ini akan sangat sulit diimplementasikan dengan cara yang performan.
Ms2ger
7
Pemilih XPath dapat melakukannya dengan metode .text () (jika Anda memilih untuk tidak menggunakan JavaScript executor).
djangofan
11
Berikut adalah contoh bagaimana Anda dapat melakukannya menggunakan xpath: // h1 [text () = 'Session'] dan Anda dapat menguji xpath di Chrome dengan mengetik $ $ ("// h1 [text () = 'Session']" ) di konsol
VinnyG
1
Ini akan sangat nyaman. Misalnya, sel tabel yang berisi tanda centang atau string "Ya", "Lulus", "OK" dll. Bisa berwarna hijau.
Andreas Rejbrand
yang $xmenjawab pertanyaan itu. untuk pertanyaan awal, $x("//td[text()='male']")lakukan triknya
Xiao

Jawaban:

394

Jika saya membaca spesifikasi dengan benar, tidak.

Anda bisa mencocokkan pada suatu elemen, nama atribut dalam elemen, dan nilai atribut bernama dalam suatu elemen. Saya tidak melihat apa pun untuk mencocokkan konten dalam suatu elemen.

Dean J
sumber
228
Ada satu kasus tepi: :empty.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
34
Untuk menjawab pertanyaan saya sendiri: Ya, itu masih berlaku untuk hari ini! Pemilih konten sudah usang! Tidak ada lagi pemilih konten sejak CSS3. ( w3.org/TR/css3-selectors/#selectors )
Wlad
158

Menggunakan jQuery:

$('td:contains("male")')
pelukis kecil
sumber
7
Akhirnya membutuhkan kebalikan dari ini, yaitu: jQuery (elemen) .not (": berisi ('string')")
Jazzy
311
Kecuali itu bukan CSS, itu JavaScript.
Synetech
9
Setuju - itu sebabnya jawaban saya mengatakan saya menggunakan jQuery, bukan CSS. Tetapi bagian dari jawaban saya telah diedit. =)
moettinger
18
tapi kata fe male sendiri juga mengandung kata 'male' no? apakah ini berfungsi hanya karena laki-laki adalah yang pertama? Bug menunggu untuk terjadi jika mereka dipesan ulang?
Michael Durrant
5
@Michael Durrant: Anda bercanda, tetapi pencocokan string elemen-silang (yang merupakan masalah yang bahkan lebih besar daripada pencocokan substring biasa dalam elemen yang sama) sebenarnya adalah salah satu alasan terbesar: berisi () dijatuhkan.
BoltClock
158

Sepertinya mereka memikirkannya untuk spesifikasi CSS3 tetapi tidak berhasil .

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

Jeff Beaman
sumber
9
Looks like they were thinking about it for the CSS3 spec but it didn't make the cut.Dan untuk alasan yang baik, itu akan melanggar seluruh premis memisahkan gaya, konten, struktur, dan perilaku.
Synetech
56
@Synetech Ini sebenarnya dapat membantu pemisahan gaya dari konten, karena itu berarti bahwa konten tidak perlu tahu tentang kliennya akan dianggap sebagai hal yang penting untuk mendasari styling. Saat ini konten html kami biasanya dipasangkan dengan erat dengan css dengan menyertakan kelas yang kami tahu sangat diperhatikan oleh styler. Mereka sudah bergeser ke arah membiarkan CSS pada konten, sebagaimana dibuktikan oleh penyeleksi nilai atribut di CSS3.
DannyMeister
8
@ Synetech dan bagaimana "perilaku", tepatnya? Bagi saya itu adalah masalah presentasi. Itu tidak melakukan apa-apa - itu menyajikan jenis konten tertentu dengan cara tertentu.
BarbaraKwarc
4
@DannyMeister, oh Anda tidak perlu meyakinkan saya. Beberapa tahun berlalu dan saya menemukan diri saya kembali ke sini ketika mencoba mencari fungsi ini dengan tepat, dan kesal karena tidak hanya tidak ada, tetapi juga ditolak. 😒 eBay baru saja mengubah UI mereka dan membuat situs mereka sangat sulit untuk digunakan. Saya mencoba memperbaikinya dengan stylesheet pengguna, tetapi markup mereka tidak membedakan antara lelang dan daftar BIN, jadi satu-satunya cara untuk menyorot jumlah tawaran dalam daftar adalah dengan mencocokkan elemen dengan konten teksnya. Yang diperlukan untuk meyakinkan seseorang adalah agar mereka mengalami skenario kasus penggunaan secara langsung.
Synetech
8
Untuk memastikan pemisahan konten dan gaya yang baik, kami tidak akan memiliki:: berisi () pemilih. Jadi, alih-alih menata sel berdasarkan kontennya (mis., Kolom "status" dari "Open", atau "Diselesaikan"), kami akan menduplikasi konten di tampilan dan atribut kelas, lalu pilih pada itu . Luar biasa!
Jon Kloske
122

Anda harus menambahkan atribut data ke baris yang disebut data-genderdengan maleatau femalenilai dan menggunakan pemilih atribut:

HTML:

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

CSS:

td[data-gender="male"] { ... }
Esteban Küber
sumber
10
Sangat oke untuk menggunakan atribut data khusus dengan Javascript dan CSS. Lihat MDN Menggunakan Atribut Data
Michael Benjamin
1
Saya setuju bahwa atribut data adalah bagaimana hal itu harus ditangani, TETAPI, aturan CSS seperti td.male seringkali lebih mudah untuk diatur, terutama dengan sudut yang dapat terlihat seperti: <td class = "{{person.gender}} ">
DJDaveMark
Gender tentu saja merupakan contoh khas di mana kelas akan bekerja. Tetapi bagaimana jika data lebih bervariasi dari sekadar maleatau female? Fakta bahwa classdiisi dengan kelas-kelas lain, urutannya tidak dijamin, mungkin ada collisi dengan nama-nama kelas lain dll membuat classtempat yang lebih buruk untuk hal-hal semacam ini. data-*Atribut khusus mengisolasi data Anda dari semua hal itu dan membuatnya lebih mudah untuk melakukan pencocokan parsial dll di atasnya menggunakan pemilih atribut.
Stijn de Witt
Ada jawaban lain dengan cuplikan kode yang berfungsi yang menggunakan atribut data dengan cara yang sama.
Josh Habdas
65

Sebenarnya ada dasar yang sangat konseptual mengapa ini belum dilaksanakan. Ini pada dasarnya adalah kombinasi dari 3 aspek:

  1. Isi teks suatu elemen secara efektif adalah anak dari elemen itu
  2. Anda tidak dapat menargetkan konten teks secara langsung
  3. CSS tidak memungkinkan untuk kenaikan dengan penyeleksi

Ketiga ini secara bersamaan berarti bahwa pada saat Anda memiliki konten teks, Anda tidak dapat naik kembali ke elemen yang mengandung, dan Anda tidak dapat menata teks saat ini. Ini mungkin signifikan karena turun hanya memungkinkan untuk pelacakan konteks tunggal dan penguraian gaya SAX. Ascending atau penyeleksi lain yang melibatkan sumbu lain memperkenalkan perlunya solusi traversal yang lebih kompleks atau serupa yang akan sangat mempersulit penerapan CSS ke DOM.

Matt Whipple
sumber
4
Ini benar. Inilah tujuan XPath. Jika Anda dapat mengeksekusi pemilih dalam JS / jQuery atau parsing library (bukan hanya CSS), maka dimungkinkan untuk menggunakan text()fungsi XPath .
Mark Thomas
Itu poin bagus tentang konten . Tetapi bagaimana saya berharap Anda dapat memiliki :contains(<sub-selector>)untuk memilih elemen yang mengandung elemen spesifik lainnya. Seperti div:contains(div[id~="bannerAd"])untuk menyingkirkan iklan dan kontainer itu.
Lawrence Dol
27

Anda bisa mengatur konten sebagai atribut data dan kemudian menggunakan pemilih atribut , seperti yang ditunjukkan di sini:

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

Anda juga dapat menggunakan jQuery untuk dengan mudah mengatur atribut data-konten:

$(function(){
  $("td").each(function(){
    var $this = $(this);
    $this.attr("data-content", $this.text());
  });
});
Buksy
sumber
3
perhatikan itu .text()dan .textContentsangat berat, cobalah untuk menghindarinya dalam daftar panjang atau teks besar bila memungkinkan. .html()dan .innerHTMLcepat.
oriadam
@JoshHabdas dapatkah Anda membuat contoh jsbin / jsfiddle dari perubahan Anda?
Buksy
1
Jika Anda akan menggunakan jQuery, gunakan pemilih berisi:$("td:contains(male)")
huwiler
Jika konten dapat diedit oleh pengguna ( contenteditable='true'- kasus saya), itu tidak cukup untuk mengatur nilai data-contentatribut sebelum halaman diberikan. Itu harus diperbarui terus menerus. Di sini yang saya gunakan:<td onkeypress="event.target.setAttribute('data-content', event.target.textContent);">
caram
13

Karena CSS tidak memiliki fitur ini, Anda harus menggunakan JavaScript untuk menata sel berdasarkan konten. Misalnya dengan XPath contains:

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

Kemudian gunakan hasilnya seperti ini:

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

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

pengguna40521
sumber
ini javascript, Bagaimana ini terkait dengan pertanyaan?
rubo77
3
Karena Anda tidak dapat memilih berdasarkan konten dalam css Anda harus menggunakan js untuk melakukan itu. Meskipun Anda benar di mana Anda menunjukkan bahwa ini bukan css, penggunaan atribut data tidak memilih berdasarkan konten. Kami hanya dapat menebak mengapa pembaca ingin memilih sel berdasarkan konten, akses apa yang ia miliki ke html, berapa banyak kecocokan, seberapa besar tabelnya, dll.
user40521
7

Saya khawatir ini tidak mungkin, karena kontennya bukan atribut dan tidak dapat diakses melalui kelas semu. Daftar lengkap pemilih CSS3 dapat ditemukan dalam spesifikasi CSS3 .

Edwin V.
sumber
4

Bagi mereka yang ingin melakukan pemilihan teks Selenium CSS, skrip ini mungkin bermanfaat.

Caranya adalah dengan memilih induk dari elemen yang Anda cari, dan kemudian mencari anak yang memiliki teks:

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

Ini akan mengembalikan elemen pertama jika ada lebih dari satu karena selalu satu elemen, dalam kasus saya.

Matas Vaitkevicius
sumber
Anda dapat menggunakan TD:contains('male')jika Anda menggunakan Selenium: saya hanya menggunakannya jika Anda tidak dapat memperbarui sumber untuk menambahkan kelas. misalnya jika Anda menguji kode warisan atau perusahaan Anda tidak akan membiarkan Anda berbicara dengan devs (eurgh!) karena tes Anda akan rapuh dan akan pecah jika seseorang mengubah teks nanti masculineatau mengubah bahasa. Dokumen SauceLabs menunjukkan penggunaan di containssini ( saucelabs.com/resources/articles/selenium-tips-css-selectors ) + cc @matas vaitkevicius apakah saya melewatkan sesuatu?
snowcode
3

Saya setuju bahwa atribut data (jawaban voyager) adalah bagaimana penanganannya, TETAPI, aturan CSS seperti:

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

seringkali dapat menjadi lebih mudah untuk diatur, terutama dengan lib sisi klien seperti angular yang dapat sesederhana:

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

Pastikan bahwa kontennya hanya satu kata! Atau Anda bahkan dapat memetakan ke berbagai nama kelas CSS dengan:

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

Untuk kelengkapan, inilah pendekatan atribut data:

<td data-gender="{{person.gender}}">
DJDaveMark
sumber
Binding template adalah solusi bagus. Tapi rasanya agak menjengkelkan untuk membuat nama-nama kelas berdasarkan pada struktur markup (meskipun itu cukup banyak yang dilakukan BEM dalam praktiknya).
Josh Habdas
2

Jawaban @ voyager tentang penggunaan data-*atribut (mis. data-gender="female|male"adalah pendekatan yang paling efektif dan standar pada 2017:

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

Hampir semua tujuan dapat dicapai karena ada beberapa penyeleksi terbatas meskipun berorientasi pada teks. Huruf :: pertama adalah elemen pseudo yang dapat menerapkan gaya terbatas pada huruf pertama elemen. Ada juga elemen :: pseudo-baris pertama selain jelas memilih baris pertama dari suatu elemen (seperti paragraf) juga menyiratkan bahwa jelas bahwa CSS dapat digunakan untuk memperluas kemampuan yang ada ini untuk merancang aspek-aspek spesifik dari textNode. .

Sampai advokasi tersebut berhasil dan diimplementasikan, hal terbaik berikutnya yang dapat saya sarankan ketika dapat diterapkan adalah untuk explode/ splitkata-kata menggunakan pembatas ruang, output setiap kata individu di dalam suatu spanelemen dan kemudian jika tujuan kata / styling dapat diprediksi digunakan dalam kombinasi dengan : n penyeleksi :

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

Lain jika tidak dapat diprediksi , lagi, jawabannya penggunaan voyager tentang menggunakan data-*atribut. Contoh menggunakan PHP:

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

Sebagian besar jawaban di sini mencoba menawarkan alternatif cara menulis kode HTML untuk memasukkan lebih banyak data karena setidaknya hingga CSS3 Anda tidak dapat memilih elemen dengan teks dalam sebagian. Tapi itu bisa dilakukan, Anda hanya perlu menambahkan sedikit vanilla JavaScript, perhatikan karena perempuan juga mengandung laki-laki itu akan dipilih:

      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
sumber
1

Saya menemukan opsi atribut untuk menjadi taruhan terbaik Anda jika Anda tidak ingin menggunakan javascript atau jquery.

Misalnya untuk menata semua sel tabel dengan kata siap, Dalam HTML lakukan ini:

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

Kemudian di css:

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

Anda juga bisa menggunakan contentdengan attr()dan menata sel tabel yang ::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 ZeroWidthSpacedigunakan untuk mengubah warna dan dapat ditambahkan menggunakan JavaScript vanilla:

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

Meskipun <td>tag akhir dapat dihilangkan dengan melakukan hal itu dapat menyebabkannya diperlakukan sebagai non-kosong.

Josh Habdas
sumber
0

Jika Anda tidak membuat DOM sendiri (misalnya dalam skrip pengguna) Anda dapat melakukan hal berikut dengan JS murni:

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>

Output konsol

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

Melakukan Widget Filter kecil seperti ini:

    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
sumber
-2

Sintaks pertanyaan ini terlihat seperti sintaks Robot Framework. Dalam hal ini, meskipun tidak ada pemilih css yang dapat Anda gunakan untuk berisi, ada kata kunci SeleniumLibrary yang dapat Anda gunakan sebagai gantinya. The Tunggu Sampai Element Berisi .

Contoh:

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