Pregunta ¿Por qué los pseudos funcionales tales como: not () y: has () permiten argumentos citados?


Aparentemente, como descubrí al comentar sobre otra respuesta, jQuery (más bien su motor selector subyacente Chisporrotear) le permite citar el argumento al :not() selector, así como la :has() selector. Esto es:

$('div:not("span")')
$('span:has("span")')

En el Selectores estándar, las comillas son siempre representativas de una cadena y nunca de un selector o palabra clave, por lo que se cita el argumento para :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)  haciendo 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:

  • Consistencia con :contains() que permite tanto argumentos cotizados como sin cotización, como se ve en la vieja 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.

  • Consistencia y facilidad de portar a sus equivalentes de método .not() y .has() (¿simplemente elimina o divide las cotizaciones externas y cambia los dos puntos a los 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)')

32
2017-09-18 10:56


origen


Respuestas:


Esto no es específico de :not(...) y :has(...) selectores, en realidad, todos los pseudos en Sizzle permiten argumentos citados. El patrón para los argumentos de pseudo Se define como:

pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)"

Que se puede encontrar en línea 91 de sizzle.js a 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" todavía 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: las comillas simples o dobles pueden ser usado alrededor del argumento en un pseudo-atributo. El escape de barra invertida es manejado adecuadamente, por lo que cualquier cadena arbitraria podría pasar como una argumento. Tenga en cuenta que la parte de "cadena" termina en el mismo índice de coincidencia como la parte "selector" en la expresión regular anterior; Entonces, en resumen, es por eso ellos son tratados por igual: porque el pseudos patrón no distinguir entre los dos. editar: a partir de jQuery 1.8.2, argumentos con y sin comillas son más explícitamente equivalentes. No puedo parecer para encontrar este código en el repositorio jQuery git [se agradecería la ayuda], pero la versión de 1.8.2 alojado por google, teniendo el sha1sum de a0f48b6ad5322b35383ffcb6e2fa779b8a5fcffc, tiene un "PSEUDO": función en línea 4206, que sí detecta explícitamente un diferencia entre los argumentos "citados" y "no incluidos", y garantiza que ambos terminan en el mismo lugar. Esta lógica no no distinguir entre el tipo de pseudo ("posicional" o no) que es el argumento para.

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 pasan a funciones. Hacer ese tipo de distinción sería posible, pero hasta donde yo sé, lo que realmente se desea es siempre fácilmente determinado 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 que distinción. (Corrija en los comentarios si hay alguna ambigua situaciones que desconozco, ¡me gustaría saber!).

Entonces, si la falta de distinción entre cadenas y selectores es una mero detalle de implementación, ¿por qué pseudos como :eq(...) explícitamente rechazar 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. El siguiente notas sobre los detalles de implementación de 1.8.1 se dejan como una 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 que :eq(...) recibe el argumento, todavía está en el forma de un argumento simple (citas y todo). diferente a :not(...), esta argumento no pasa por una compile(...) fase. El "rechazo" de el argumento inválido es en realidad debido a la vía de acceso directo +argument, que dará como resultado NaN para cualquier cadena citada (que en a su vez, nunca coincide con nada). Esta es otra implementación más detalle, aunque en este caso un comportamiento "correcto" (de nuevo, en la medida de lo posible como soy consciente. ¿Hay situaciones donde los argumentos no numéricos a tales las funciones deberían coincidir de hecho?)

editar: A partir de jQuery 1.8.2, las cosas se han refactoreado un tanto, y Los pseudos "posicionales" ya no reciben el argumento "crudo". Como resultado, los argumentos citados ahora son aceptados :eq(...) y similares. Este cambio parece haber sido un efecto secundario de otra corrección de errores, ya que no hay mención de soporte para argumentos cotizados en el registro de cambios para af8206ff .., que estaba destinado a arreglar un error en el manejo :first y :last, error de jQuery # 12303. Este compromiso se encontró usando git bisect y una secuencia de comandos phantomjs relativamente simple. Es notable que después de la reescritura 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") el apoyo no es un comportamiento intencionado.

De hecho, hay razones con respecto a los filtros personalizados, cuyos argumentos En algunos casos, se podría pensar que son cadenas y, a veces, como selectores, no importa a qué se parecen superficialmente, dependiendo de el método en el que se evalúan ... pero eso se acerca el pedante Debería bastar con decir que no tiene una distinción al menos hace las cosas más simples cuando se llaman funciones que, no importa lo que puedan representar, espera una representación de cadena.

En resumen, toda la situación puede considerarse como una implementación detalle, y tiene su raíz en el hecho de que los selectores se pasan como cuerdas en primer lugar (¿de qué otra manera las harías entrar en Sizzle?).


30
2017-09-19 22:47