2012-09-03 15 views
5

sto provando a fare un jQuery ui calendar effettuare chiamate ajax quando si fa clic su una data, ma ho avuto un problema qualche giorno fa. Ho trovato uno snippet di codice che presumibilmente lo fa, ma come ho scoperto utilizza selettori personalizzati jQuery. Il codice mi ha dato un errore quindi ho iniziato a scavare nei selettori personalizzati per saperne di più su di loro. Finora non sono stato in grado di scoprire perché ho questo strano comportamento.jQuery selettore personalizzato, "undefined"

Ecco una foto si spera di chiarire le cose, spiegherò di più dopo che enter image description here

ho digitato nella console

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

E come vedete che io meta2 e stack2 sono indefiniti e un'altra cosa strana, perché index2 restituisce un documento #, si suppone che contenga l'indice della matrice di elementi.

Inoltre l'elemento (el2) non è nemmeno l'elemento giusto. Date un'occhiata, io chiamo

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

questo dovrebbe selezionare tutte le date dal calendario, e nel primo ciclo, console.log dovrebbe stampare questo

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

ma invece ottengo il primo tag "a" nell'intero documento, in questo caso è quello del mese precedente (come mostrato nell'immagine).

Se qualcuno può gettare un po 'di luce su questa situazione, si prega di fare. Oh, e un'altra cosa che ho forgout circa

meta2, la sua supposta per contenere questo

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

e di nuovo nel mio caso la sua indefinita ...

voglio condividere il mio codice javascript spero che aiuta

<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

ho corso per lo stesso problema questa mattina trascorso 2 ore, per me la meta è ancora indefinita. Funzionava perfettamente prima, ti prego tienimi aggiornato se troverai qualcosa. Grazie. –

+0

Ciao @JakubKuchar, non sono riuscito a farlo funzionare, quindi ho risolto il problema e selezionato l'elemento di cui avevo bisogno in un altro modo. Ma comunque sono davvero interessato a conoscere la risposta. – Jordashiro

+3

Sizzle è stato aggiornato in jQuery 1.8, con conseguenti modifiche sintattiche significative alla creazione del selettore personalizzato. Si prega di guardare la documentazione sfrigolare per ulteriori informazioni. https://github.com/jquery/sizzle/wiki/Sizzle-Documentation Sembra che se si aggiorni a jQuery 1.8.1, i vecchi selettori funzioneranno di nuovo. –

risposta

6

La spiegazione più breve è "jQuery 1.8.0 ha un bug in esso, l'aggiornamento a 1.8.1 per la correzione", ma che non abbastanza rispondere a tutto.

jQuery 1.8.x ha un motore "Sizzle" significativamente aggiornato, che è quello che utilizza per i selettori. Il modo in cui i selettori personalizzati vengono chiamati è stato modificato come parte di questo cambiamento, ma inoltre il modo in cui molti selettori vengono elaborati è stato modificato. Diverse ipotesi sull'ordine in cui le regole vengono elaborate non sono più valide, ecc. È anche significativamente più veloce in vari casi d'uso.

Anche quando si esegue l'aggiornamento a 1.8.1, si vedrà comunque che le cose sembrano un po 'diverse dal modo in cui l'hanno fatto in 1.7.2 (l'ultimo nella serie pre-1.8.x) durante l'elaborazione dell'esempio fornito . Questo spiega cosa stai vedendo nella selezione di "il primo < un elemento > nella pagina". Vale a dire: le vostre aspettative su come funzionano i selettori personalizzati non sono come funzionano effettivamente, e se avete permesso al ciclo di continuare (piuttosto che chiamare "debugger" alla prima iterazione), vedreste che sta effettivamente attraversando all <a> elementi). In breve: Sizzle non garantisce quale ordine verranno chiamate le varie regole, solo che il risultato corrisponderà a tutte.

Se si è certi che la propria regola personalizzata sarà meno efficiente di altre regole (forse perché si è certi che altre regole possano ridurre drasticamente il numero di elementi corrispondenti), è possibile forzarne l'esecuzione prima selezionandole, quindi chiamando .find() solo su quel sottoinsieme di elementi, ad esempio:

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

quanto riguarda la "pila" essere indefinito, come Kevin B rileva, se l'aggiornamento 1.8.1 ripristina retrocompatibilità, l'API è cambiato, e sembra che "stack" non sia passato più nello pseudo. Questo ha un senso, davvero, a causa dell'ordine alterato in cui possono essere chiamati i test. Cioè: lo stack è vuoto nel momento in cui lo raggiungi, perché "vedi se uno degli elementi <a> corrisponde a questo pseudo-selettore" è la prima regola che viene elaborata. I test dovrebbero sempre essere autonomi, quindi lo stack non sarebbe davvero molto utile in ogni caso (potrebbe solo portare a confusione).

Quindi, se 1.8.1 ripristina la compatibilità all'indietro, qual è il metodo compatibile con i precedenti per la creazione di pseudo-selettori? Come si può vedere in the documentation for Sizzle's pseudo-selectors, il metodo preferito per creare pseudo-selettori come da jQuery 1.8 è il metodo "createPseudo" ($ .expr.createPseudo), che preferisce utilizzare una chiusura anziché l'argomento "meta". Quindi, per i vostri esempi particolari, i "nuovi" modi di fare loro sarebbe:

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

dove "tomatch" è l'argomento al: prova (...) selettore. Come puoi vedere, gli argomenti extra che stavi cercando non sono più necessari in questa nuova sintassi. Per quanto riguarda qualcosa di un po 'più utile:

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

Questa versione di "esattamente" dovrebbe essere compatibile con 1.8+, ed è il metodo di fare le cose preferito *.

Penso che anche con il bump in jQuery version/api, il codice che hai fornito ancora non farà esattamente quello che vuoi, dato che il datepicker può essere ricostruito sul capriccio del plugin. C'è ancora un breve momento in cui puoi dire che gli elementi desiderati sono effettivamente evidenziati come previsto, quindi il selettore stesso sembra funzionare.

Un esempio completo è riportato di seguito, in base ai campioni forniti. Puoi vedere le differenze di comportamento cambiando la versione jQuery usata tra 1.7.2, 1.8.0 e 1.8.1. Per la compatibilità tra le versioni, è stato aggiunto un test per $ .expr.createPseudo alle assegnazioni della funzione pseudo-selettore. Nota che tutti i "debugger"; le dichiarazioni sono state commentate, poiché avere un punto di interruzione a ogni iterazione attraverso tutti i link di data nel datepicker diventa piuttosto noioso, e la chiamata getJSON è stata derisa per consentire al test di essere autonomo.

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

Spero che questo aiuti a far luce sulle cose.

* Dico che questo è il metodo "preferito", ma si sta ancora utilizzando "eval". Questo è altamente scoraggiato, poiché è lento e può portare a risultati inaspettati/sorprendenti/pericolosi. Un modo migliore per costruire regex sulla base di una stringa sarebbe

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

Anche se anche questo ha dei problemi, come "s" può contenere RegExp speciali-caratteri. In questo caso particolare, però, un espressione regolare non è nemmeno necessario, e le cose possono essere testati in modo più efficiente attraverso:

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

È anche possibile salvare un po 'di più facendo rotolare la conversione in chiusura, dando la piena funzionalità di:

$.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

jquery ui datepicker ha ganci per questo tipo di funzionalità. Invece di cercare di indirizzare gli elementi DOM che compongono le date, dovresti associarti al comportamento di selezionarne uno. jsFiddle

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

di modifica per un commento: se è necessario per lo stile di una data particolare, allora si dovrebbe indirizzare esso mediante l'applicazione di una classe personalizzata prima di essere disegnato. jsFiddle

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

sì, vero. Ma ho bisogno e sto usando il selettore di jquery personalizzato per le date di evidenziazione ... per esempio. –

+0

Certo, quindi puoi applicare una classe personalizzata ad una data prima che venga disegnata usando la callback 'beforeShowDay', e poi scegli come target quella classe. In conclusione, usa sempre le API di jquery UI per prime, sono estese. – Sinetheta