Чи є селектор CSS для елементів, що містять певний текст?


510

Я шукаю селектор CSS для наступної таблиці:

Peter    | male    | 34
Susanne  | female  | 12

Чи є селектор, який відповідає всім ТД, що містять "чоловічий"?


1
Проблема полягає в тому, що це було б дуже важко реалізувати на виконанні.
Ms2ger

7
Селектор XPath може це зробити методом .text () (якщо ви не бажаєте використовувати виконавця JavaScript).
djangofan

11
Ось приклад того, як це можна зробити за допомогою xpath: // h1 [text () = 'Session'], і ви можете протестувати xpath в Chrome, набравши $ x ("// h1 [text () = 'Session']") ) в консолі
VinnyG

1
Це було б так зручно. Наприклад, комірка таблиці, що містить галочку або рядки "Так", "Передати", "ОК" тощо, може бути зеленим.
Андреас Рейбранд

$xвідповідає на питання. на первісне запитання, $x("//td[text()='male']")чи трюк
Сяо

Відповіді:


392

Якщо я правильно прочитав специфікацію , ні.

Ви можете зіставити елемент, ім'я атрибута в елементі та значення назви атрибута в елементі. Я не бачу нічого для відповідності вмісту в межах елемента.


226
Існує один крайній випадок: :empty.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

34
Щоб відповісти на моє власне запитання: Так, це все ще актуально на сьогодні! Вибір вмісту застарілий! Від CSS3 більше не вибрано вміст. ( W3.org/TR/css3-selectors/#selectors )
Wlad

158

Використання jQuery:

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

7
Закінчилося, що потрібно протилежне цьому, а саме: jQuery (element) .not (": містить ('string')")
Jazzy

309
За винятком того, що це не CSS, це JavaScript.
Synetech

9
Погоджено - тому моя відповідь сказала, що я використовую jQuery, а не CSS. Але та частина моєї відповіді була відредагована. =)
moettinger

17
але саме слово fe male містить також слово 'male' ні? це працює просто 'тому, що чоловіки спочатку? Помилка чекає, якщо станеться повторне замовлення?
Майкл Дюррант

5
@Michael Durrant: Ви жартома, але узгодження рядків між елементами (що є навіть більшою проблемою, ніж звичайні збіги підрядків у тому ж елементі) насправді є однією з найбільших причин: падіння містить ().
BoltClock

158

Схоже, вони думали про це для специфікації CSS3, але це не вирішило .

:contains()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.І з поважної причини це порушило б усю передумову розділення стилів, змісту, структури та поведінки.
Synetech

56
@Synetech Це насправді може допомогти відокремити стилізацію від контенту, оскільки це означає, що контенту не потрібно знати про свого клієнта, він вважатиме важливим на основі стилізації. Зараз наш html-вміст, як правило, щільно з'єднаний з css, включаючи класи, про які ми знаємо, що стайлер піклується. Вони вже рухаються у напрямку дозволу CSS до вмісту, про що свідчать селектори значень атрибутів у CSS3.
DannyMeister

8
@Synetech і як саме це "поведінка"? Для мене це питання презентації. Він нічого не робить - він певним чином презентує певні типи вмісту.
BarbaraKwarc

4
@DannyMeister, о, не треба мене переконувати. Через пару років я опиняюся тут, намагаючись шукати саме цю функціональність, і засмучується, що її не тільки не існує, але її відкинули. 😒 eBay просто змінив інтерфейс і зробив їх сайт дуже важким у використанні. Я намагаюся виправити це за допомогою таблиці стилів користувачів, але їх розмітка не розрізняє аукціони та списки BIN, тому єдиний спосіб виділити кількість ставок у списку - це зіставлення елемента за його текстовим вмістом. Все, що потрібно для того, щоб переконати когось, це те, що вони переживають сценарій використання з перших рук.
Synetech

8
Щоб забезпечити належне розділення вмісту та стилів, у нас не буде вибору: містить (). Отже, замість стилізації комірки на основі її вмісту (наприклад, стовпець "статус" "Відкрити" або "Розв’язано"), ми будемо дублювати вміст як на дисплеї, так і на атрибутах класу, а потім виберемо на цьому . Відмінно!
Джон Клоске

122

Ви повинні були б додати атрибут даних в рядках , званих data-genderз maleабо femaleзначенням і використовувати селектор атрибуту:

HTML:

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

CSS:

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

10
Цілком нормально використовувати спеціальні атрибути даних за допомогою Javascript та CSS. Дивіться MDN з використанням атрибутів даних
Michael Benjamin

1
Я погоджуюся, що атрибут даних полягає в тому, як з ним слід обробляти, АЛЕ правило CSS типу td.male часто набагато простіше налаштувати, особливо з кутовим, який може виглядати приблизно так: <td class = "{{person.gender}} ">
DJDaveMark

Стать - це, звичайно, типовий приклад, коли працюватимуть заняття. Але що робити, якщо дані більш різноманітні, ніж просто maleабо female? Те, що classзаповнено іншими класами, порядок їх не гарантується, можуть виникнути колізії з іншими назвами класів тощо, які classще гірші місця для подібних матеріалів. Виділений data-*атрибут ізолює ваші дані від усіх цих матеріалів та полегшує їх часткове узгодження тощо за допомогою селекторів атрибутів.
Штійн де Вітт

Є ще одна відповідь з фрагментом робочого коду, який використовує атрибути даних аналогічно.
Джош Хабдас

65

Насправді існує дуже концептуальна основа, чому це не було здійснено. Це в основному 3 аспекти:

  1. Текстовий вміст елемента фактично є дочірнім елементом цього елемента
  2. Не можна безпосередньо націлювати текст на вміст
  3. CSS не допускає сходження з селекторами

Ці 3 разом означають, що до моменту отримання текстового вмісту ви вже не можете повернутися до елемента, що містить, і ви не можете стилізувати даний текст. Це, ймовірно, є важливим, оскільки низхідне значення дозволяє лише відстежувати контекст та аналіз стилю SAX. Вихідні чи інші селектори, що включають інші осі, вводять необхідність у складніших обходах або подібних рішеннях, які значно ускладнюють застосування CSS до DOM.


4
Це правда. Це те, для чого XPath. Якщо ви можете виконати селектор в JS / jQuery або бібліотеці розбору (на відміну від лише CSS), можна було б використовувати text()функцію XPath .
Марк Томас

Це хороший момент щодо змісту . Але як би я хотів, щоб у вас було :contains(<sub-selector>)можливість вибрати елементи, які містять інші конкретні елементи. Як div:contains(div[id~="bannerAd"])би позбутися реклами та її контейнера.
Лоуренс Дол

27

Ви можете встановити вміст як атрибут даних, а потім використовувати селектори атрибутів , як показано тут:

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

Ви також можете використовувати jQuery для легкого встановлення атрибутів вмісту даних:

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

3
зауважте, що .text()і .textContentвони досить важкі, намагайтеся уникати їх у довгих списках чи великих текстах, коли це можливо. .html()і .innerHTMLшвидко.
oriadam

@JoshHabdas Ви можете зробити приклад jsbin / jsfiddle для вашої зміни?
Buksy

1
Якщо ви збираєтесь використовувати jQuery, використовуйте перемикач містить:$("td:contains(male)")
huwiler

Якщо вміст редагується користувачем ( contenteditable='true'- мій випадок), недостатньо встановити значення data-contentатрибута до того, як сторінка буде надана. Його потрібно постійно оновлювати. Ось чим я користуюся:<td onkeypress="event.target.setAttribute('data-content', event.target.textContent);">
caram

13

Оскільки у CSS відсутня ця функція, вам доведеться використовувати JavaScript для стилю комірок за вмістом. Наприклад з XPath contains:

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

Потім використовуйте результат так:

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

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


це javascript, як це пов'язано з питанням?
rubo77

3
Оскільки ви не можете вибирати за вмістом у css, вам доведеться використовувати js для цього. Хоча ви прямо там, де зазначаєте, що це не css, використання атрибутів даних не вибирається за змістом. Ми можемо лише здогадуватися, чому читач хоче вибрати клітинки за вмістом, який доступ вона має до html, скільки збігів, наскільки велика таблиця і т. Д. Тощо
user40521

7

Боюся, що це неможливо, тому що вміст не є атрибутом, і він не доступний через псевдоклас. Повний список селекторів CSS3 можна знайти в специфікації CSS3 .


4

Для тих, хто хоче зробити підбір тексту CSS Selenium, цей скрипт може бути корисним.

Хитрість полягає в тому, щоб вибрати батьківський елемент елемента, який ви шукаєте, а потім знайти дитину, яка містить текст:

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

Це поверне перший елемент, якщо його буде більше, оскільки це завжди один елемент, в моєму випадку.


Ви можете використовувати, TD:contains('male')якщо ви використовуєте Selenium: imho використовувати його, лише якщо ви не можете оновити джерело для додавання класів. Наприклад, якщо ви тестуєте застарілий код або ваша компанія не дозволяє вам говорити з дияволами (eurgh!), оскільки ваші тести будуть крихкими і зламаються, якщо хтось змінить текст пізніше masculineабо змінить мову. Документи SauceLabs показують використання containsтут ( saulabs.com/resources/articles/selenium-tips-css-selectors ) + cc @matas vaitkevicius я щось пропустив?
снігопад

3

Я погоджуюся, що атрибут даних (відповідь voyager) полягає в тому, як з ним слід обробляти, АЛЕ, такі правила CSS:

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

часто може бути набагато простіше налаштувати, особливо з клієнтськими вкладками на зразок angularjs, який може бути таким же простим, як:

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

Просто переконайтеся, що вміст - це лише одне слово! Або ви навіть можете зіставити різні назви класів CSS за допомогою:

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

Для повноти ось підхід до атрибутів даних:

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

Встановлення шаблонів - чудове рішення. Але створювати назви класів на основі структури розмітки (хоча це майже все, що робить BEM на практиці).
Джош Хабдас

2

Відповідь @ voyager про використання data-*атрибуту (наприклад, data-gender="female|male"це найефективніший та стандартний підхід станом на 2017 рік:

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

Досить більшості цілей можна досягти, оскільки є деякі, хоча і обмежені селектори, орієнтовані навколо тексту. :: першою літерою є псевдо-елементом , який може застосувати обмежений стиль до першої букви елемента. Існує також псевдоелемент :: першого рядка, окрім очевидного вибору першого рядка елемента (наприклад, абзацу), також випливає, що очевидно, що CSS може бути використаний для розширення цієї існуючої можливості для стилювання конкретних аспектів textNode .

Поки таке пропагування не буде успішним і не буде реалізовано наступне найкраще, що я міг би запропонувати, коли це застосовано, до explode/ splitслів, використовуючи роздільник пробілу, вивести кожне окреме слово всередині spanелемента, а потім, якщо слово / стильова мета передбачувано використовувати у поєднанні з : n-ми селекторами :

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

Якщо ще не передбачувано , знову ж таки, використовуйте відповідь voyager про використання data-*атрибута. Приклад використання PHP:

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


1

Більшість відповідей тут намагаються запропонувати альтернативу, як написати HTML-код, щоб включити більше даних, оскільки принаймні до CSS3 ви не можете вибрати елемент за допомогою часткового внутрішнього тексту. Але це можна зробити, потрібно лише додати трохи ванільного JavaScript, зауважте, оскільки самка також містить чоловічого, він буде обраний:

      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

Я вважаю, що варіант атрибуту є вашою найкращою ставкою, якщо ви не хочете використовувати javascript або jquery.

Наприклад, щоб стилізувати всі комірки таблиці зі словом готовим, у HTML зробіть це:

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

Потім у css:

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

0

Крім того, можна використовувати contentз attr()і елементів таблиці стилів, які ::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використовується для зміни кольору і може бути доданий за допомогою ванільного JavaScript:

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

Незважаючи на те, <td>що кінцевий тег s може бути пропущений, це може спричинити його обробку як порожню.


0

Якщо ви не створюєте DOM самостійно (наприклад, у користувальницькому сценарії), ви можете зробити наступне з чистим 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>

Вихід консолі

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

-2

Виконайте невеликі віджети фільтру на зразок цього:

    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

Синтаксис цього питання виглядає як синтаксис Robot Framework. У цьому випадку, хоча не існує селектора css, який ви можете використовувати для містить, є ключове слово SeleniumLibrary, яке ви можете використовувати замість цього. Зачекайте , поки елемент містить .

Приклад:

Wait Until Element Contains  | ${element} | ${contains}
Wait Until Element Contains  |  td | male
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.