javascript - tag - ¿Por qué los pseudos funcionales tales como: not() y: has() permiten argumentos citados?
title of page html (1)
Aparentemente, como he descubierto al comentar otra respuesta , jQuery (en lugar de su motor de selección subyacente Sizzle ) le permite citar el argumento al selector :not()
así como también al selector :has()
. A saber :
$(''div:not("span")'')
$(''span:has("span")'')
En el estándar Selectores , las comillas son siempre representativas de una cadena y nunca de un selector o una palabra clave, por lo tanto, citar el argumento a :not()
siempre es inválido. Esto no cambiará en Selectores 4.
También puede ver que es una sintaxis no estándar agregando un selector de CSS no compatible , como :nth-last-child(1)
hace que el selector falle por completo :
$(''div:not("span"):nth-last-child(1)'')
$(''span:has("span"):nth-last-child(1)'')
¿Hay alguna buena razón, técnica o de otro tipo, para permitir citas aquí? Las únicas posibilidades que se me ocurren son:
Coherencia con
:contains()
que permite los argumentos entre comillas y sin comillas, como se ve en la antigua especificación de Selectores . Excepto:contains()
acepta cadenas / palabras clave, no selectores ...Coherencia con la implementación de pseudos personalizados usando
$.expr['':'']
, que siempre permite argumentos entre comillas y sin comillas.La consistencia y la facilidad de portar a sus homólogos de método
.not()
y.has()
(¿simplemente eliminar o dividir las cotizaciones externas y cambiar dos puntos a puntos?).
Pero no puedo encontrar ninguna fuente para apoyarlos u oponerme a ellos. De hecho, la capacidad de citar argumentos de selector no está documentada en ninguna parte tampoco, ni parece haber ninguna diferencia entre cotizar y no citar el argumento:
$(''div:not(span)'')
$(''span:has(span)'')
Esto no es específico de :not(...)
y :has(...)
selectores: en realidad, todos los pseudos en Sizzle permiten argumentos entrecomillados. El patrón para los argumentos de pseudos se define como:
pseudos = ":(" + characterEncoding + ")(?://((?:([''/"])((?:////.|[^////])*?)//2|([^()[//]]*|(?:(?:" + attributes + ")|[^:]|////.)*|.*))//)|)"
Que se puede encontrar en la línea 91
de sizzle.js
partir de 831c9c48...
Agreguemos algo de sangría a eso, para que sea un poco más legible. Desafortunadamente, esta sigue siendo una expresión regular, por lo que "un poco más legible" aún deja mucho que desear:
pseudos = (
":(" + characterEncoding + ")" +
"(?:" +
"//(" + // literal open-paren
"(?:" +
"([''/"])" + // literal open-quote
"((?:////.|[^////])*?)" + // handle backslash escaping
"//2" + // close-quote
"|" + // - OR -
"(" +
"[^()[//]]*" +
"|" +
"(?:" +
"(?:" + attributes + ")" +
"|" +
"[^:]" +
"|" +
"////." +
")*" +
"|" +
".*" +
")" +
")" +
"//)" + // literal close-paren
"|" + // ie, ''or nothing''
")"
);
La principal conclusión de esto es: se pueden usar comillas simples o dobles alrededor del argumento en un pseudo-atributo. El escape de barra invertida se maneja adecuadamente, por lo que cualquier cadena arbitraria se podría pasar como un argumento. Tenga en cuenta que la parte "string" termina en el mismo índice de coincidencia que la parte "selector" en la expresión regular anterior; así que, en resumen, es por eso que son tratados por igual: porque el patrón pseudos
no distingue entre los dos. editar: a partir de jQuery 1.8.2, los argumentos con y sin comillas son más explícitamente equivalentes. Parece que no puedo encontrar este código en el repositorio jQuery git [la ayuda sería apreciada], pero la versión de 1.8.2 alojada por google, con el sha1sum de a0f48b6ad5322b35383ffcb6e2fa779b8a5fcffc , tiene una función "PSEUDO":
en la línea 4206
, que lo hace explícitamente detectar una diferencia entre los argumentos "citados" y "sin cita", y asegura que ambos terminen en el mismo lugar. Esta lógica no distingue entre el tipo de pseudo ("posicional" o no) para el cual es el argumento.
Como Sizzle usa cadenas de Javascript para iniciar el proceso de selección, no hay distinción entre "cadena" y "selector" cuando los argumentos se transfieren a las funciones. Hacer ese tipo de distinción sería posible, pero hasta donde yo sé, lo que realmente se desea siempre se determina fácilmente desde el contexto más básico (es decir: qué tipo de pseudo se está utilizando), por lo que no hay una razón real para hacer la distinción (Corrija en los comentarios si hay situaciones ambiguas que desconozco, ¡me gustaría saber!) .
Entonces, si la falta de distinción entre cadenas y selectores es un mero detalle de implementación, ¿por qué los pseudos tales como :eq(...)
rechazan explícitamente tales selecciones?
La respuesta es simple: no lo hace, realmente. Al menos, no a partir de jQuery 1.8.1. [ Editar: a partir de jQuery 1.8.2, no lo hace en absoluto. Los argumentos de los pseudos "posicionales" pueden citarse como cualquier otra cosa. Las notas a continuación sobre los detalles de implementación de 1.8.1 se dejan como curiosidad histórica.
Funciones tales como :eq(...)
se implementan como:
"eq": function( elements, argument, not ) {
var elem = elements.splice( +argument, 1 );
return not ? elements : elem;
}
En el momento en que :eq(...)
recibe el argumento, todavía está en la forma de un argumento simple (comillas y todo). A diferencia :not(...)
, este argumento no pasa por una fase de compile(...)
. El "rechazo" del argumento no válido se debe en realidad al atajo de +argument
, que dará como resultado NaN
para cualquier cadena entrecomillada (que a su vez, nunca coincide con nada). Este es otro detalle más de la implementación, aunque en este caso se trata de un comportamiento "correcto" ( una vez más, hasta donde yo sé. ¿Hay situaciones en las que los argumentos no numéricos para tales funciones de hecho deberían coincidir?)
editar: A partir de jQuery 1.8.2, las cosas se han refactoreado un poco, y los pseudos "posicionales" ya no reciben el argumento "en bruto". Como resultado, los argumentos citados ahora se aceptan en :eq(...)
y similares. Este cambio parece haber sido un efecto secundario de otra corrección de errores , ya que no hay mención de compatibilidad para argumentos cotizados en el registro de cambios para af8206ff.. , que estaba destinado a corregir un error en el manejo :first
y :last
, jQuery error # 12303 . Este compromiso se encontró usando git bisect
y un script phantomjs relativamente simple . Es notable que después de la reescritura de Sizzle en e89d06c4.. , Sizzle no solo fallaría silenciosamente para selectores como :eq("3")
, en realidad arrojaría una excepción. Eso debería tomarse como una evidencia más de que :eq("3")
apoyo :eq("3")
no es el comportamiento previsto.
De hecho, existen razones con respecto a los filtros personalizados, cuyos argumentos podrían en algunos casos ser considerados como cadenas, y a veces como selectores, sin importar cómo se vean superficialmente, dependiendo del método en el que se evalúan ... pero eso se acerca el pedante Debería bastar con decir que no tener una distinción al menos facilita las cosas al llamar funciones que, sin importar lo que representen, esperan una representación de cadena.
En resumen, se puede pensar en toda la situación como un detalle de la implementación, y se basa en el hecho de que los selectores se pasan como cadenas en primer lugar (¿de qué otra forma los obtendría en Sizzle?).