¿Hay un selector CSS para elementos que contienen cierto texto?

512

Estoy buscando un selector CSS para la siguiente tabla:

Peter    | male    | 34
Susanne  | female  | 12

¿Hay algún selector que coincida con todos los TD que contengan "masculino"?

jantimon
fuente
1
El problema es que esto sería muy difícil de implementar de manera eficiente.
Ms2ger
77
Un selector XPath puede hacerlo con el método .text () (si prefiere no usar el ejecutor de JavaScript).
djangofan
11
Aquí hay un ejemplo de cómo puedes hacerlo usando xpath: // h1 [text () = 'Session'] y puedes probar xpath en Chrome escribiendo $ x ("// h1 [text () = 'Session']" ) en la consola
VinnyG
1
Esto sería muy conveniente. Por ejemplo, una celda de la tabla que contiene una marca de verificación o la cadena "Sí", "Pasar", "Aceptar", etc. podría ser verde.
Andreas Rejbrand
Las $xrespuestas a la pregunta. para la pregunta original, $x("//td[text()='male']")¿el truco
Xiao

Respuestas:

394

Si leo la especificación correctamente, no.

Puede coincidir en un elemento, el nombre de un atributo en el elemento y el valor de un atributo con nombre en un elemento. Sin embargo, no veo nada para hacer coincidir el contenido dentro de un elemento.

Dean J
fuente
228
Hay un caso extremo: :empty.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
34
Para responder a mi propia pregunta: Sí, ¡todavía es cierto por hoy! ¡Los selectores de contenido quedaron en desuso! No más selectores de contenido desde CSS3. ( w3.org/TR/css3-selectors/#selectors )
Wlad
158

Usando jQuery:

$('td:contains("male")')
Moettinger
fuente
77
Terminé necesitando lo contrario de esto, que es: jQuery (elemento) .not (": contiene ('cadena')")
Jazzy
311
Excepto que no es CSS, es JavaScript.
Synetech
99
De acuerdo, por eso mi respuesta dice que estaba usando jQuery, no CSS. Pero esa parte de mi respuesta fue editada. =)
moettinger
18
pero la palabra fe masculino también contiene la palabra 'hombre' no? ¿Funciona solo porque el hombre es el primero? ¿Error esperando a suceder si se reordenan?
Michael Durrant
55
@Michael Durrant: Usted bromea, pero la coincidencia de cadenas de elementos cruzados (que es un problema aún mayor que las coincidencias de subcadenas regulares dentro del mismo elemento) es, de hecho, una de las principales razones: contiene () se eliminó.
BoltClock
158

Parece que estaban pensando en ello para la especificación CSS3 pero no logró el corte .

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

Jeff Beaman
fuente
99
Looks like they were thinking about it for the CSS3 spec but it didn't make the cut.Y por una buena razón, violaría toda la premisa de separar el estilo, el contenido, la estructura y el comportamiento.
Synetech
56
@Synetech En realidad, puede ayudar a la separación del estilo del contenido, ya que significa que el contenido no necesita saber sobre su cliente que se considerará importante para basar el estilo. En este momento, nuestro contenido html generalmente está estrechamente emparejado con el CSS al incluir clases que sabemos que al estilista le importan. Ya están cambiando hacia dejar CSS en el contenido, como lo demuestran los selectores de valor de atributo en CSS3.
DannyMeister
8
@Synetech y cómo es exactamente "comportamiento"? Para mí es una cuestión de presentación. No hacer nada - que presenta ciertos tipos de contenido de una determinada manera.
BarbaraKwarc
44
@DannyMeister, oh, no tienes que convencerme. Un par de años después, me encuentro de nuevo aquí mientras trato de buscar exactamente esta funcionalidad, y me molesta que no solo no exista, sino que fue rechazada. 😒 eBay acaba de cambiar su interfaz de usuario e hizo que su sitio fuera muy difícil de usar. Estoy tratando de arreglarlo con una hoja de estilo de usuario, pero su marcado no distingue entre subastas y listados de BIN, por lo que la única forma de resaltar el número de ofertas en una lista es haciendo coincidir el elemento con su contenido textual. Todo lo que se necesita para convencer a alguien es que experimente un caso de uso de primera mano.
Synetech
8
Para asegurar una buena separación de contenido y estilo, no tendremos un selector: contiene (). Por lo tanto, en lugar de diseñar una celda en función de su contenido (por ejemplo, una columna de "estado" de "Abrir" o "Resuelto"), duplicaremos el contenido tanto en la pantalla como en los atributos de clase, y luego seleccionaremos . ¡Excelente!
Jon Kloske
122

Tendría que agregar un atributo de datos a las filas llamadas data-gendercon un valor maleo femaley usar el selector de atributos:

HTML:

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

CSS:

td[data-gender="male"] { ... }
Esteban Küber
fuente
10
Está perfectamente bien usar atributos de datos personalizados con Javascript y CSS. Ver MDN usando atributos de datos
Michael Benjamin
1
Estoy de acuerdo en que el atributo de datos es cómo debe manejarse, PERO, una regla CSS como td.male a menudo es mucho más fácil de configurar, especialmente con angular, que podría verse así: <td class = "{{person.gender}} ">
DJDaveMark
El género es, por supuesto, un ejemplo típico donde las clases funcionarían. Pero, ¿qué pasa si los datos son más variados que solo maleo female? El hecho de que classesté lleno de otras clases, el orden de ellas no está garantizado, puede haber colisiones con otros nombres de clase, etc., hace classque este lugar sea peor. Un data-*atributo dedicado aísla sus datos de todas esas cosas y facilita la coincidencia parcial, etc., utilizando selectores de atributos.
Stijn de Witt
Hay otra respuesta con un fragmento de código de trabajo que utiliza atributos de datos de manera similar.
Josh Habdas
65

En realidad, existe una base muy conceptual de por qué esto no se ha implementado. Es una combinación de básicamente 3 aspectos:

  1. El contenido de texto de un elemento es efectivamente hijo de ese elemento.
  2. No puede orientar el contenido de texto directamente
  3. CSS no permite la ascensión con selectores

Estos 3 juntos significan que cuando tiene el contenido del texto, no puede ascender de nuevo al elemento contenedor y no puede darle estilo al texto actual. Esto es probablemente significativo ya que descender solo permite un seguimiento singular del contexto y el análisis de estilo SAX. Los selectores ascendentes u otros que involucran otros ejes introducen la necesidad de soluciones transversales más complejas o similares que complicarían en gran medida la aplicación de CSS al DOM.

Matt Whipple
fuente
44
Esto es verdad. Para eso es XPath. Si puede ejecutar el selector en JS / jQuery o una biblioteca de análisis (en lugar de solo CSS), entonces sería posible usar la text()función XPath .
Mark Thomas
Ese es un buen punto sobre el contenido . Pero cómo desearía poder :contains(<sub-selector>)seleccionar un elemento que contenga otros elementos específicos. Desea div:contains(div[id~="bannerAd"])deshacerse del anuncio y su contenedor.
Lawrence Dol
27

Puede configurar el contenido como atributo de datos y luego usar selectores de atributos , como se muestra aquí:

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

También puede usar jQuery para configurar fácilmente los atributos de contenido de datos:

$(function(){
  $("td").each(function(){
    var $this = $(this);
    $this.attr("data-content", $this.text());
  });
});
Buksy
fuente
3
tenga en cuenta que .text()y .textContentson bastante pesados, trate de evitarlos en listas largas o textos grandes cuando sea posible. .html()Y .innerHTMLson rápidos.
oriadam
@JoshHabdas, ¿puedes hacer un ejemplo de jsbin / jsfiddle de tu cambio?
Buksy el
1
Si va a usar jQuery, use el selector de contenidos:$("td:contains(male)")
huwiler el
Si el usuario puede editar el contenido ( contenteditable='true'- mi caso), no es suficiente establecer el valor del data-contentatributo antes de que se renderice la página. Tiene que ser actualizado continuamente. Aquí lo que uso:<td onkeypress="event.target.setAttribute('data-content', event.target.textContent);">
caram
13

Como CSS carece de esta característica, tendrá que usar JavaScript para diseñar las celdas por contenido. Por ejemplo con XPath's contains:

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

Luego usa el resultado así:

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

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

usuario40521
fuente
Esto es JavaScript. ¿Cómo se relaciona esto con la pregunta?
rubo77
3
Como no puede seleccionar por contenido en css, tendrá que usar js para hacerlo. Si bien está justo donde señala que esto no es CSS, el uso de un atributo de datos no se selecciona por contenido. Sólo podemos adivinar por qué el lector quiere seleccionar celdas de contenido, el tipo de acceso que tenga respecto al HTML, cuántas coincidencias, lo grande que es la mesa, etc, etc
user40521
7

Me temo que esto no es posible, porque el contenido no es un atributo ni es accesible a través de una pseudo clase. La lista completa de selectores CSS3 se puede encontrar en la especificación CSS3 .

Edwin V.
fuente
4

Para aquellos que buscan hacer selecciones de texto Selenium CSS, este script podría ser de alguna utilidad.

El truco consiste en seleccionar el elemento primario del elemento que está buscando y luego buscar el elemento secundario que tiene el texto:

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

Esto devolverá el primer elemento si hay más de uno, ya que siempre es un elemento, en mi caso.

Matas Vaitkevicius
fuente
Puedes usarlo TD:contains('male')si estás usando Selenium: solo lo usaré si no puedes actualizar la fuente para agregar clases. por ejemplo, si está probando código heredado o su empresa no le permite hablar con los desarrolladores (¡eurgh!) porque sus pruebas serán frágiles y se romperán si alguien cambia el texto más adelante masculineo cambia el idioma. Los documentos de SauceLabs muestran el uso de containsaquí ( saucelabs.com/resources/articles/selenium-tips-css-selectors ) + cc @matas vaitkevicius ¿Me he perdido algo?
código de nieve
3

Estoy de acuerdo en que el atributo de datos (respuesta del viajero) es cómo debe manejarse, PERO, reglas CSS como:

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

a menudo puede ser mucho más fácil de configurar, especialmente con libs del lado del cliente como angularjs, que podría ser tan simple como:

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

¡Solo asegúrate de que el contenido sea solo una palabra! O incluso podría asignar a diferentes nombres de clase CSS con:

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

Para completar, aquí está el enfoque de atributos de datos:

<td data-gender="{{person.gender}}">
DJDaveMark
fuente
Los enlaces de plantillas son una gran solución. Pero se siente un poco complicado crear nombres de clases basados ​​en la estructura de marcado (aunque eso es más o menos lo que BEM hace en la práctica).
Josh Habdas
2

La respuesta de @ voyager sobre el uso de data-*atributos (por ejemplo, data-gender="female|male"es el enfoque más efectivo y compatible con los estándares a partir de 2017:

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

Casi la mayoría de los objetivos se pueden alcanzar ya que hay algunos selectores, aunque limitados, orientados alrededor del texto. La :: primera letra es un pseudo-elemento que puede aplicar un estilo limitado a la primera letra de un elemento. También hay una :: primera línea pseudo-elemento , además de seleccionar obviamente la primera línea de un elemento (como un párrafo) también implica que es obvio que CSS podría usarse para extender esta capacidad existente para diseñar aspectos específicos de un nodo de texto .

Hasta que dicha promoción tenga éxito y se implemente, lo mejor que podría sugerir cuando corresponda es a explode/ splitpalabras usando un deliminador de espacio, envíe cada palabra individual dentro de un spanelemento y luego, si el objetivo de palabra / estilo es predecible, use en combinación con : enésimo selectores :

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

De lo contrario, si no es predecible , nuevamente, use la respuesta del viajero sobre el uso del data-*atributo. Un ejemplo usando PHP:

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

La mayoría de las respuestas aquí intentan ofrecer una alternativa a cómo escribir el código HTML para incluir más datos porque al menos hasta CSS3 no puede seleccionar un elemento por texto interno parcial. Pero se puede hacer, solo necesita agregar un poco de JavaScript vainilla, tenga en cuenta que ya que la hembra también contiene el macho, se seleccionará:

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

Creo que la opción de atributo es su mejor opción si no desea usar javascript o jquery.

Por ejemplo, para diseñar todas las celdas de la tabla con la palabra lista, en HTML haga esto:

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

Luego en css:

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

También puede usar contentcon attr()y las celdas de tabla de estilo que son ::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 ZeroWidthSpacese usa para cambiar el color y se puede agregar usando JavaScript de vainilla:

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

Aunque se puede omitir la<td> etiqueta de finalización s, al hacerlo puede tratarse como no vacía.

Josh Habdas
fuente
0

Si no crea el DOM usted mismo (por ejemplo, en un script de usuario), puede hacer lo siguiente con JS puro:

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>

Salida de la consola

text: <td> Peter
text: <td> male
text: <td> 34
text: <td> Susanne
text: <td> female
text: <td> 12
male: <td text="male"> male
GeroldBroser restablece a Mónica
fuente
-2

Hacer pequeños widgets de filtro como este:

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

La sintaxis de esta pregunta se parece a la sintaxis de Robot Framework. En este caso, aunque no hay un selector css que pueda usar para contiene, hay una palabra clave SeleniumLibrary que puede usar en su lugar. El elemento Esperar hasta que contenga .

Ejemplo:

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