2012-09-03 15 views
5

Estoy tratando de hacer un calendario jQuery ui hacer llamadas ajax cuando se hace clic en una fecha, pero me encontré con un problema hace unos días. Encontré un fragmento de código que supuestamente hace esto, pero como descubrí que usa los selectores personalizados de jQuery. El código me dio un error, así que empecé a buscar en los selectores personalizados para obtener más información sobre ellos. Hasta ahora no he podido averiguar por qué tengo este comportamiento extraño.jQuery selector personalizado, "indefinido"

Aquí está una foto de esperar a aclarar las cosas, voy a explicar más después de que enter image description here

he escrito en la consola

$('.ui-datepicker-calendar td a:test(3)') 

Y como ves i Meta2 y STACK2 no están definidos y una cosa más extraña, ¿por qué index2 devuelve un documento #, se supone que contiene el índice de la matriz de elementos.

Además, el elemento (el2) ni siquiera es el elemento correcto. Echa un vistazo, yo llamo

$('.ui-datepicker-calendar td a:test(3)')

esto se supone que seleccionar todas las fechas del calendario, y en el primer bucle, console.log debe imprimir este

<td class=" ui-datepicker-week-end " data-handler="selectDay" data-event="click" data-month="8" data-year="2012"><a class="ui-state-default" href="#">1</a></td> 

pero en vez Obtengo la primera etiqueta "a" en todo el documento, en este caso es la del mes anterior (como se ve en la imagen).

Si alguien puede arrojar algo de luz sobre esta situación, por favor hazlo. Ah, y una cosa más que forgout sobre

meta2, su supuesta contener esta

[ 
    ':test(argument)', // full selector 
    'test',   // only selector 
    '',    // quotes used 
    'argument'   // parameters 
] 

y otra vez en mi caso su indefinido ...

voy a compartir mi código javascript espero que ayuda

<script> 
    $(function() 
    { 
     $.expr[":"].test = function(el2,index2,meta2,stack2) 
     { 
      debugger; 
      console.log(el2); 
      console.log(index2); 
      console.log(meta2); 
      console.log(stack2); 
     } 
    }) 

    $(function() 
    { 
     function getJsonDate(year, month) 
     { 
      $.getJSON('dates.php?year='+year+'&month='+month, function(data) 
      { 
       var i = 0; 
       for (i = 0; i < data.data.length; i++) 
       { 
        debugger; 
        var myDay = data.data[i]['d']; 
        $('.ui-datepicker-calendar td a:exactly('+data.data[i]['d']+')') 
        .css({color: '#f00'}) 
        .attr('href',data.data[i]['link']) 
        .parent().attr('onclick',''); 
       } 
      }); 
     } 
     $.expr[":"].exactly = function(el, index, meta, stack) 
     { 
      debugger; 
      console.log(el); 
      console.log(index); 
      console.log(meta); 
      console.log(stack); 
      var s = meta[3]; 
      if (!s) return false; 
      return eval("/^" + s + "$/i").test($(el).text()); 
     }; 
     $('#datepicker').datepicker(
     { 
      inline: true, 
      onSelect: function(dateText, inst) 
      { 
       Date.prototype.toString = function() {return isNaN (this) ? 'NaN' : [this.getDate(), this.getMonth(), this.getFullYear()].join('/')} 
       d = new Date(dateText); 
       getJsonDate(d.getFullYear(), d.getMonth()+1); 
      }, 
      onChangeMonthYear: function(year, month, inst) 
      { 
       //alert(year); 
       //alert(month); 
       getJsonDate(year, month); 
      } 
     }); 
    }); 
</script> 
+0

corrí al mismo problema esta mañana pasó 2h, para mí meta aún no está definido. Estaba funcionando perfectamente antes, por favor mantenme actualizado si encuentras algo. Gracias. –

+0

Hola @JakubKuchar, no logré que funcione, así que hice una solución y seleccioné el elemento que necesitaba de otra manera. Pero aun así estoy realmente interesado en saber la respuesta. – Jordashiro

+3

Sizzle se actualizó en jQuery 1.8, lo que generó importantes cambios de sintaxis en la creación del selector personalizado. Mire la documentación de chisporroteo para más información. https://github.com/jquery/sizzle/wiki/Sizzle-Documentation Parece que si actualiza a jQuery 1.8.1, los selectores antiguos volverán a funcionar. –

Respuesta

6

La explicación más corta es "jQuery 1.8.0 tiene un error en ella, actualice a 1.8.1 para el arreglo", pero que no acaba de responder a todo.

jQuery 1.8.x tiene un motor "Sizzle" significativamente mejorado, que es lo que usa para los selectores. La forma en que se llaman los selectores personalizados se ha alterado como parte de este cambio, pero además se ha alterado la forma en que se procesan muchos selectores. Varias suposiciones sobre el orden en que se procesan las reglas ya no son ciertas, etc. También es significativamente más rápido en varios casos de uso.

Incluso al actualizar a la versión 1.8.1, aún verá que las cosas se ven bastante diferentes de como lo hicieron en 1.7.2 (la última versión de la serie anterior a la 1.8.x) al procesar el ejemplo que proporcionó . Esto explica lo que está viendo en la selección de "el primer elemento <a> en la página". Es decir: su expectativa de cómo funcionan los selectores personalizados no es cómo realmente funcionan, y si permitió que el ciclo continuara (en lugar de llamar a "depurador" en la primera iteración), vería que en realidad está pasando por todos <a> elementos). En resumen: Sizzle no garantiza en qué orden se llamarán las distintas reglas, solo que el resultado las igualará a todas.

Si está seguro de que su regla personalizada será menos eficiente que otras reglas (quizás porque está seguro de que otras reglas pueden reducir drásticamente la cantidad de elementos coincidentes), puede forzarlas a ejecutar primero seleccionándolas, a continuación, llamar .find() en sólo que subconjunto de elementos, por ejemplo:

$(".ui-datepicker-calendar").find("td a:test(3)"); 

cuanto a la "pila" está sin definir, como Kevin B señala, aunque la actualización 1.8.1 restaura compatibilidad hacia atrás, el API ha cambiado, y parece que "stack" simplemente ya no pasa al pseudo. Esto tiene sentido, en realidad, debido al orden alterado en que se pueden llamar las pruebas. Es decir: la pila está vacía en el momento en que la alcanzas, porque "ver si alguno de los elementos <a> coincide con este pseudo-selector" es la primera regla que se procesa. Las pruebas siempre deben ser autocontenidas, por lo que la pila no sería realmente útil de todos modos (solo puede generar confusión).

Entonces, si 1.8.1 restaura la compatibilidad con versiones anteriores, ¿cuál es el método compatible con versiones anteriores para crear pseudo-selectores? Como puede ver en the documentation for Sizzle's pseudo-selectors, el método preferido para crear pseudo-selectores a partir de jQuery 1.8 es el método "createPseudo" ($ .expr.createPseudo), que prefiere usar un cierre en lugar del argumento "meta". Así que para sus ejemplos particulares, las "nuevas" formas de hacer ellos serían:

$.expr[":"].test = $.expr.createPseudo(function(tomatch) 
{ 
     return function(el2) 
     { 
      debugger; 
      console.log(el2); // not much else to see here 
     }; 
}) 

donde "tomatch" es el argumento al: prueba (...) Selector. Como puede ver, los argumentos adicionales que estaba buscando ya no son necesarios en esta nueva sintaxis. En cuanto a algo un poco más útil:

$.expr[":"].exactly = $.expr.createPseudo(function(s) 
{ 
    return function(el) 
    { 
     if(!s) return false; 
     return eval("/^" + s + "$/i").test($(el).text()); 
    }; 
}); 

Esta versión de "exactamente" debe ser compatible con 1.8+, y es el método preferido * de hacer las cosas.

Creo que incluso con el bache en la versión jQuery/api, el código que proporcionó todavía no hará exactamente lo que quiere, ya que es probable que el datepicker se reconstruya por el capricho del complemento. Todavía hay un breve momento en el que puede ver que los elementos deseados se resaltan de la manera prevista, por lo que el selector en sí parece estar funcionando.

A continuación encontrará un ejemplo completo, basado en las muestras que proporcionó. Puede ver las diferencias en el comportamiento cambiando la versión de jQuery utilizada entre 1.7.2, 1.8.0 y 1.8.1. Para la compatibilidad entre versiones, se ha agregado una prueba para $ .expr.createPseudo a las asignaciones de función de pseudo-selector. Tenga en cuenta que todo "depurador"; las declaraciones han sido comentadas, ya que tener un punto de corte en cada iteración en todos los enlaces de fecha en datepicker es bastante tedioso, y la llamada a getJSON ha sido burlada para permitir que la prueba sea independiente.

<html> 
<head> 
    <title>jQuery custom selector, "undefined"</title> 
    <!-- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script> --> 
    <!-- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.js"></script> --> 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.js"></script> 
    <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.js"></script> 
    <link rel="stylesheet" 
     href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/themes/base/jquery-ui.css" /> 
    <script> 
    $(function() 
    { 
     $.expr[":"].test = $.expr.createPseudo ? 
      $.expr.createPseudo(function(tomatch) 
      { 
       return function(el2) 
       { 
//     debugger; 
        console.log(el2); 
       }; 
      }) : 
      function(el2,index2,meta2,stack2) 
      { 
    //   debugger; 
       console.log(el2); 
       console.log(index2); 
       console.log(meta2); 
       console.log(stack2); 
      }; 
    }) 

    $(function() 
    { 
     function getJsonDate(year, month) 
     { 
      //$.getJSON('dates.php?year='+year+'&month='+month, function(data) 
      //{ 
       var data = {data:[ 
        {d:1,link:"a"}, 
        {d:15,link:"b"}, 
        {d:25,link:"c"} 
       ]}; 
       var i = 0; 
       for (i = 0; i < data.data.length; i++) 
       { 
//     debugger; 
        var myDay = data.data[i]['d']; 
        $('.ui-datepicker-calendar td a:exactly('+data.data[i]['d']+')'). 
         css({color: '#f00'}). 
         attr('href',data.data[i]['link']). 
         parent().attr('onclick',''); 
       } 
      //}); 
     } 

     $.expr[":"].exactly = $.expr.createPseudo ? 
      $.expr.createPseudo(function(s) 
      { 
       return function(el) 
       { 
        if(!s) return false; 
        return eval("/^" + s + "$/i").test($(el).text()); 
       }; 
      }) : 
      function(el, index, meta, stack) 
      { 
//    debugger; 
       console.log(el); 
       console.log(index); 
       console.log(meta); 
       console.log(stack); 
       var s = meta[3]; 
       if (!s) return false; 
       return eval("/^" + s + "$/i").test($(el).text()); 
      }; 

     $('#datepicker').datepicker(
     { 
      inline: true, 
      onSelect: function(dateText, inst) 
      { 
       Date.prototype.toString = function() { 
        return isNaN (this) ? 
         'NaN' : 
         [this.getDate(), this.getMonth(), this.getFullYear()].join('/') 
       } 
       d = new Date(dateText); 
       getJsonDate(d.getFullYear(), d.getMonth()+1); 
      }, 
      onChangeMonthYear: function(year, month, inst) 
      { 
       //alert(year); 
       //alert(month); 
       getJsonDate(year, month); 
       return false; 
      } 
     }); 
    }); 
    </script> 
    <script> 
    (function($){$(function(){ 
     $("<input />"). 
      attr({type:"button", value: "run test selector"}). 
      click(function(){ 
       $(".ui-datepicker-calendar td:test(3) a"); 

       // Or, if you are certain that your test will be less-efficient than an exclusion based 
       // on parents, you could do: 
       // $(".ui-datepicker-calendar").find("td a:test(3)"); 
      }). 
      appendTo("body"); 
    })}(window.jQuery)); 
    </script> 
</head> 
<body> 
    <a href="#ignoreThisLink">.</a> 
    <a href="#ignoreThisToo">.</a> 
    <p> 
     (first, click the input box to cause the datepicker to initialise) 
    </p> 
    <input type="text" id="datepicker" /> 
</body> 
</html> 

Espero que eso ayude a arrojar algo de luz sobre las cosas.

* Digo que este es el método "preferido", pero todavía está utilizando "eval". Esto es altamente desaconsejado, ya que es lento y puede conducir a resultados inesperados/sorprendentes/peligrosos. Una mejor manera de construir expresiones regulares en base a una cadena sería

return (new RegExp("^" + s + "$", "i")).test($(el).text()); 

aunque incluso esto no tiene problemas, como "s" puede contener RegExp especiales caracteres. En este caso particular, sin embargo, una expresión regular no es ni siquiera necesario, y las cosas se puede probar mucho más eficiente a través de:

return String(s).toUpperCase() === $(el).text().toUpperCase(); 

Incluso puede guardar un poco más rodando la conversión en el cierre, que le da la función completa de:

$.expr[":"].exactly = $.expr.createPseudo(function(s) 
{ 
     if(!s) return function(){ return false; }; 
     s = String(s).toUpperCase(); 

     return function(el) 
     { 
      return (s === $(el).text().toUpperCase()); 
     }; 
}); 
4

El jquery ui datepicker tiene ganchos para este tipo de funcionalidad. En lugar de tratar de orientar los elementos DOM que componen las fechas, debe vincularse al comportamiento de seleccionar uno. jsFiddle

$('#datepicker').datepicker({ 
    onSelect: function(dateText, inst){ 
     //awesome ajax stuff based on dateText 
    } 
});​ 

de edición para hacer comentarios: si necesita el estilo de una fecha en particular, entonces se debe orientar mediante la aplicación de una clase personalizada antes de que se dibuja. jsFiddle

$('#datepicker').datepicker({ 
    beforeShowDay: function(date){ 
     return [true, 'date-' + date.getDate() ]; 
    } 
}); 
+0

sí, cierto. Pero necesito y estoy usando selector de jquery personalizado para resaltar las fechas ... por ejemplo. –

+0

Claro, entonces puede aplicar una clase personalizada a una fecha antes de que se dibuje utilizando la devolución de llamada 'beforeShowDay', y luego solo apunte a esa clase. En pocas palabras, siempre use primero las API de jquery, son extensas. – Sinetheta