Есть ли CSS-селектор для элементов, содержащих определенный текст?

512

Я ищу селектор CSS для следующей таблицы:

Peter    | male    | 34
Susanne  | female  | 12

Есть ли какой-либо селектор для соответствия всем TD, содержащим «мужской»?

jantimon
источник
1
Проблема заключается в том, что это было бы очень трудно реализовать эффективным способом.
Ms2ger
7
Селектор XPath может сделать это с помощью метода .text () (если вы предпочитаете не использовать JavaScript-исполнитель).
Джангофан
11
Вот пример того, как вы можете сделать это, используя xpath: // h1 [text () = 'Session'], и вы можете проверить xpath в Chrome, набрав $ x ("// h1 [text () = 'Session']" ) в консоли
VinnyG
1
Это было бы так удобно. Например, ячейка таблицы, содержащая галочку или строку «Да», «Пропустить», «ОК» и т. Д., Может быть зеленой.
Андреас Рейбранд
$xотвечает на вопрос. на оригинальный вопрос, $x("//td[text()='male']")делает трюк
Сяо

Ответы:

394

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

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

Дин Дж
источник
228
Существует один крайний случай: :empty.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
34
Чтобы ответить на мой собственный вопрос: да, это все еще верно на сегодня! Селекторы контента устарели! С CSS3 больше нет селекторов контента. ( W3.org/TR/css3-selectors/#selectors )
Wlad
158

Используя jQuery:

$('td:contains("male")')
moettinger
источник
7
Закончился тем, что нуждался в обратном: jQuery (элемент) .not (": contains ('string')")
Jazzy
311
За исключением того, что это не CSS, это JavaScript.
Synetech
9
Согласен - вот почему мой ответ сказал, что я использую jQuery, а не CSS. Но эта часть моего ответа была отредактирована. =)
moettinger
18
но само слово « мужчина » также содержит слово «мужчина», нет? это работает только потому, что мужчина первый? Ждут ошибки, если они будут переупорядочены?
Майкл Даррант
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
Чтобы обеспечить хорошее разделение содержимого и стиля, у нас не будет селектора: contains (). Таким образом, вместо стилизации ячейки на основе ее содержимого (например, столбца «status» «Open» или «Resolved»), мы продублируем содержимое как в атрибутах display, так и в атрибутах класса, а затем выберем этот элемент. , Отлично!
Джон Клоске
122

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

HTML:

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

CSS:

td[data-gender="male"] { ... }
Эстебан Кюбер
источник
10
Вполне нормально использовать пользовательские атрибуты данных с Javascript и CSS. См. MDN с использованием атрибутов данных
Майкл Бенджамин,
1
Я согласен, что атрибут data - это то, как он должен обрабатываться, НО, правило CSS, такое как td.male, часто гораздо проще настроить, особенно с angular, который может выглядеть примерно так: <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());
  });
});
Buksy
источник
3
обратите внимание , что .text()и .textContentдовольно тяжелый, стараются избегать их в длинных списках или больших текстов , когда это возможно. .html()и .innerHTMLбыстро.
Ориадам
@JoshHabdas, можете ли вы сделать jsbin / jsfiddle пример вашего изменения?
Букси
1
Если вы собираетесь использовать jQuery, используйте селектор содержимого: $("td:contains(male)")
содержимого
Если содержимое редактируется пользователем ( contenteditable='true'- в моем случае), недостаточно установить значение data-contentатрибута до отображения страницы. Он должен постоянно обновляться. Вот что я использую:<td onkeypress="event.target.setAttribute('data-content', event.target.textContent);">
Карам
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/

user40521
источник
это javascript, как это связано с вопросом?
rubo77
3
Поскольку вы не можете выбирать по содержимому в CSS, вам придется использовать JS, чтобы сделать это. Хотя вы и правы, отметив, что это не CSS, использование атрибута данных не выбирается по содержимому. Мы можем только догадываться, почему читатель хочет выбирать ячейки по содержанию, какой у него доступ к html, сколько совпадений, какой
размер
7

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

Эдвин В.
источник
4

Для тех, кто хочет сделать выделение текста Selenium CSS, этот скрипт может быть полезным.

Хитрость заключается в том, чтобы выбрать родительский элемент для элемента, который вы ищете, а затем найти дочерний элемент с текстом:

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 используйте его, только если вы не можете обновить исходный код для добавления классов. Например, если вы тестируете устаревший код или ваша компания не позволит вам поговорить с разработчиками (да!), потому что ваши тесты будут хрупкими и сломаются, если кто-то позже изменит текст на masculineили изменит язык. Документы SauceLabs показывают использование containsздесь ( saucelabs.com/resources/articles/selenium-tips-css-selectors ) + cc @matas vaitkevicius Я что-то пропустил?
снежный код
3

Я согласен, что атрибут data (ответ 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}}">
DJDaveMark
источник
Шаблонные привязки - отличное решение. Но создание названий классов на основе структуры разметки кажется немного сложным (хотя это практически то же, что делает БЭМ на практике).
Джош Хабдас
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 end может быть опущен , это может привести к тому, что он будет рассматриваться как непустой.

Джош Хабдас
источник
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
GeroldBroser восстанавливает Монику
источник
-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')
            }
        })
    })
campino2k
источник
-2

Синтаксис этого вопроса выглядит как синтаксис Robot Framework. В этом случае, хотя нет никакого селектора css, который вы можете использовать для содержит, есть ключевое слово SeleniumLibrary, которое вы можете использовать вместо этого. Подождите , пока элемент не содержит .

Пример:

Wait Until Element Contains  | ${element} | ${contains}
Wait Until Element Contains  |  td | male
Эфтихия Томаиду
источник