5

日付がクリックされたときに jQuery ui カレンダーが ajax 呼び出しを行うようにしようとしていますが、数日前に問題が発生しました。おそらくこれを行うコードのスニペットを見つけましたが、jQueryカスタムセレクターを使用していることがわかりました。コードでエラーが発生したので、カスタム セレクターを調べて詳細を調べました。これまでのところ、なぜこの奇妙な動作が発生するのかを見つけることができませんでした。

これがうまくいけば物事を解決するための写真です。後で詳しく説明します ここに画像の説明を入力

コンソールに入力しました

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

そして、ご覧のとおり、meta2 と stack2 は未定義であり、もう 1 つ奇妙なことがあります。なぜ index2 は #document を返すのでしょうか。これには、要素の配列のインデックスが含まれているはずです。

さらに、要素 (el2) は正しい要素ではありません。見てください、私は電話します

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

これはカレンダーからすべての日付を選択することになっており、最初のループで console.log がこれを出力するはずです

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

しかし、代わりに、ドキュメント全体で最初の「a」タグを取得します。この場合は、前月のタグです (図に示すように)。

誰かがこの状況に少し光を当てることができるなら、してください。ああ、もう一つ忘れていること

meta2 、これが含まれているはずです

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

繰り返しますが、私の場合は未定義です...

私のjavascriptコードを共有します。

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

2 に答える 2

6

最短の説明は「jQuery 1.8.0 にはバグがあります。修正するには 1.8.1 にアップグレードしてください」ですが、それですべてが解決されるわけではありません。

jQuery 1.8.x には、大幅にアップグレードされた「Sizzle」エンジンがあり、これがセレクターに使用されます。この変更の一環として、カスタム セレクターの呼び出し方法が変更されましたが、さらに多くのセレクターの処理方法が変更されました。ルールが処理される順序に関するさまざまな仮定が当てはまらなくなったなどです。また、さまざまなユースケースで大幅に高速化されています。

1.8.1 にアップグレードした場合でも、提供した例を処理すると、1.7.2 (1.8.x 以前のシリーズの最新版) の場合とはかなり異なって見えることがわかります。これは、「ページの最初の <a> 要素」の選択で何が表示されているかを説明しています。つまり、カスタムセレクターがどのように機能するかについての期待は、実際にどのように機能するかではなく、(最初の反復で「debugger;」を呼び出すのではなく) ループを続行できるようにすると、実際にループが実行されていることがわかります。すべての<a> 要素)。要するに: Sizzle は、さまざまなルールが呼び出される順序を保証しません。結果がすべてのルールに一致することだけを保証します。

カスタム ルールが他のルールよりも効率が悪いことが確実な場合 (おそらく、他のルールが一致する要素の数を大幅に減らす可能性があることが確実なため)、それらを選択してから .find を呼び出すことで、これらを強制的に実行することができます。 () 要素のそのサブセットだけで、例えば:

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

「スタック」が定義されていないことについては、Kevin B が指摘しているように、1.8.1 の更新で下位互換性が復元されますが、API が変更され、「スタック」が疑似に渡されなくなったようです。これは、テストが呼び出される順序が変更されているため、実際には理にかなっています。つまり、「 <a> 要素のいずれかがこの疑似セレクターに一致するかどうかを確認する」が処理される最初のルールであるため、スタックに到達した時点でスタックは空です。テストは常に自己完結型である必要があるため、スタックは実際にはあまり役に立ちません (混乱を招くだけかもしれません)。

では、1.8.1 が下位互換性を回復した場合、疑似セレクターを作成するための上位互換性のある方法は何ですか? Sizzle の疑似セレクターのドキュメントでわかるように、jQuery 1.8 の時点で疑似セレクターを作成するための推奨される方法は、"createPseudo" メソッド ($.expr.createPseudo) です。 " 口論。したがって、特定の例では、それらを行う「新しい」方法は次のようになります。

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

ここで、「tomatch」は :test(...) セレクターへの引数です。ご覧のとおり、探していた追加の引数は、この新しい構文では不要になりました。もう少し便利なものについては:

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

このバージョンの「正確に」は 1.8+ と互換性があり、推奨される*方法です。

jQuery のバージョン / API が増えても、提供されたコードは、プラグインの気まぐれで datepicker が再構築される可能性があるため、希望どおりに動作しないと思います。目的の要素が実際に意図したとおりに強調表示されていることを確認できる瞬間がまだ少しあるため、セレクター自体が機能しているように見えます。

あなたが提供したサンプルに基づいて、完全な例を以下に示します。1.7.2、1.8.0、および 1.8.1 の間で使用される jQuery のバージョンを変更すると、動作の違いを確認できます。バージョン間の互換性のために、$.expr.createPseudo のテストが擬似セレクター関数の割り当てに追加されました。すべて「デバッガー」であることに注意してください。ステートメントはコメントアウトされています。これは、datepicker のすべての日付リンクにわたる各反復でブレークポイントを設定するのがかなり面倒になるためです。また、テストを自己完結型にするために getJSON 呼び出しがモック化されています。

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

物事に光を当てるのに役立つことを願っています。

*これは「好ましい」方法だと言いますが、あなたはまだ「eval」を使用しています。これは遅く、予期しない/驚くべき/危険な結果につながる可能性があるため、非常に推奨されません。文字列に基づいて正規表現を構築するより良い方法は

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

「s」には正規表現の特殊文字が含まれている可能性があるため、これでも問題があります。ただし、この特定のケースでは、正規表現は必要なく、次の方法でより効率的にテストできます。

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

変換をクロージャーにロールバックすることで、もう少し節約することもできます。これにより、次のすべての機能が提供されます。

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

        return function(el)
        {
            return (s === $(el).text().toUpperCase());
        };
});
于 2012-09-12T20:13:57.360 に答える
4

jquery ui datepickerには、この種の機能のためのフックがあります。日付を構成するDOM要素をターゲットにしようとするのではなく、1つを選択する動作にバインドする必要があります。jsFiddle

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

コメント用に編集:特定の日付のスタイルを設定する必要がある場合は、描画する前にカスタムクラスを適用してターゲットにする必要があります。jsFiddle

$('#datepicker').datepicker({
    beforeShowDay: function(date){
        return [true, 'date-' + date.getDate() ];
    }
});
于 2012-09-11T21:00:58.663 に答える