質問

いくつかの JavaScript コードに奇妙な問題があります (繰り返しますが、私は JS コードのデバッグが嫌いです)。私は通常のテーブルに取り組んでいます。これはJSON呼び出しから埋められ、いくつかのページング(2xページングと呼ぶことができると思います)、並べ替え、および行の選択のサポートを追加しました。すべてがうまく機能しています。ただし、行が DESELECTED (および選択解除のみ) されると、add_navigate イベントが 2 回発生します。その結果、不要なデータが少しリロードされ、さらに不要なロードが示されます。

まず、私のJSコードは次のとおりです。

var customerType;
var selYear;
var selMonth;
var sdir;
var sort;
var page;
var noteId;
var hasDoneCall;
var customerId;

var customerIdChanged = false;


function initValues() {
    customerType = "Publisher";
    selYear = new Date().getFullYear();
    selMonth = new Date().getMonth()+1;
    sdir = false;
    sort = "CustomerName";
    page = 1;
    noteId = false;
    customerId = 0;

    hasDoneCall = location.href.indexOf('#') > 0;

}
function flash(elm, color, duration) {

    var current = elm.css('backgroundColor');

    elm.animate({ backgroundColor: 'rgb(' + color + ')' }, duration / 2).animate({ backgroundColor: current }, duration / 2);

}

function createNotes(elm) {
    var btn = jQuery(elm);
    btn.attr('disabled', 'disabled');
    bulkCreditOption('true', '', function(changeSet) {
        var i = 0;
        while (i < changeSet.length) {
            var selector = "input[type=checkbox][value=" + changeSet[i] + "].check:checked";
            var row = jQuery(selector).parent().parent();
            var cell = row.find("td:nth-child(2)");

            cell.html("<a href=\"javascript:showNotes('" + changeSet[i] + "')\">" + cell.html() + "</a>");
            flash(row, '60, 130, 200', 500);
            i++;
        }
        btn.removeAttr('disabled');
    });
}

function deleteNotes(elm) {
    var btn = jQuery(elm);
    btn.attr('disabled', 'disabled');
    bulkCreditOption('', 'true', function(changeSet) {
        var i = 0;
        while (i < changeSet.length) {
            var selector = "input[type=checkbox][value=" + changeSet[i] + "].check:checked";
            var row = jQuery(selector).parent().parent();
            var cell = row.find("td:nth-child(2)");

            cell.html(cell.text());
            flash(row, '60, 130, 200', 500);
            i++;
        }
        btn.removeAttr('disabled');
    });
}

function bulkCreditOption(createNotes, deleteNotes, callback) {
    var path = "/BulkCredit";
    var data = "";
    var checked = jQuery("input[type=checkbox].check:checked");
    checked.each(function(chk) {
        data += "&ids=" + urlencode(jQuery(this).val());

    });
    jQuery.ajax({
        type: 'POST',
        url: path,
        dataType: 'json',
        data: "createNotes=" + urlencode(createNotes) + data + "&deleteNotes=" + urlencode(deleteNotes),
        success: function(msg) {
            callback(msg);
        }
    });
}

initValues();

Sys.Application.add_init(function() {
    Sys.Application.add_navigate(function(sender, e) {
        var reinstate = e.get_state();
        if (typeof (reinstate) != 'undefined' && typeof (reinstate.customerType) != 'undefined') {
            customerType = reinstate.customerType;
            selYear = reinstate.selYear;
            selMonth = reinstate.selMonth;
            sdir = reinstate.sdir;
            sort = reinstate.sort;
            page = reinstate.page;
            noteId = reinstate.noteId;
            customerId = reinstate.customerId;

        } else {
            initValues();
        }

        if (!customerIdChanged) {

            jQuery("#customerTypeChanger").val(customerType);

            jQuery("#customerFilter").val(customerId);

            jQuery("#monthPicker").empty();

            makeMonthPicker();

            if (noteId != false && noteId != 'false') {
                doShowNotes();
            } else {
                jQuery("#notesContent").hide();
                jQuery("#tableContent").show();
                doAjaxCall();
            }
        } else {
            //logic to fetch customer specific stuff here, TODO
            customerIdChanged = false;
        }
    });
    Sys.Application.set_enableHistory(true);


    jQuery(document).ready(function() {
        origColor = jQuery("#dataTable > thead > tr > th").css('backgroundColor');

        makeMonthPicker();

        jQuery("#customerTypeChanger").val(customerType);
        jQuery("#customerTypeChanger").change(function() {
            customerType = jQuery(this).val();
            iqSetHistory();
        });

        jQuery("#customerFilter").change(function() {
            customerId = jQuery(this).val();
            var tableBody = jQuery("#dataTable > tbody");
            tableBody.find("tr").removeClass("selected");
            tableBody.find("tr[rel=" + customerId + "]").addClass("selected");
            customerIdChanged = true;
            iqSetHistory();
        });

        jQuery(".checkAll").click(function() {
            var elm = jQuery(this);
            if (elm.is(':checked')) {
                jQuery(".check").attr('checked', 'checked');
            } else {
                jQuery(".check").removeAttr('checked');
            }
        });

        if (!hasDoneCall) {
            if (noteId == false) {
                doAjaxCall();
            } else {
                doShowNotes();
            }
        }

    });
});

function makeMonthPicker() {
    var selDate = new Date();
    selDate.setFullYear(selYear);
    selDate.setMonth(selMonth-1);
    jQuery("#monthPicker").monthPicker(function(year, month) {
        selYear = year;
        selMonth = month;
        iqSetHistory();
    }, selDate);
}

var origColor;
var notesPath = "/ShowNotes";


function fadeOut(elm) {
    elm.animate({ backgroundColor: 'rgb(180, 180, 180)' }, 250);
}
function fadeIn(elm) {
    elm.animate({ backgroundColor: origColor }, 250);
}

function iqSetHistory() {
    var state = { 'customerType': customerType, 'selYear': selYear, 'selMonth': selMonth, 'sdir': sdir, 'sort': sort, 'page': page, 'noteId': noteId, 'customerId':customerId };

    Sys.Application.addHistoryPoint(state);
}
var ajaxPath = "/GetCreditListMonth";
function doAjaxCall() {

    fadeOut(jQuery("#dataTable > thead > tr > th"));
    jQuery.ajax({
        type: "POST",
        url: ajaxPath,
        dataType: "json",
        data: "month=" + selMonth + "&year=" + selYear + "&custType=" + customerType + "&sort=" + sort + "&sdir=" + sdir + "&page=" + page + "&asCsv=false",
        success: function(msg) {
            var table = jQuery("#dataTable");

            var tableBody = table.find("tbody");


            tableBody.empty();

            var i = 0;
            while (i < msg.Rows.length) {
                var data = msg.Rows[i];
                var row = jQuery("<tr rel=\"" + data.CustomerId + "\"></tr>");

                if (data.CustomerId == customerId) {
                    row.addClass("selected");
                }

                if (i % 2 == 1) {
                    row.addClass("alternatetablerow");
                }

                var custName = data.CustomerName;
                if (data.PaymentCreated) {
                    custName = "<a href=\"javascript:showNotes('" + getCreditId(data.CustomerId) + "')\">" + custName + "</a>";
                }
                row.append("<td><input type=\"checkbox\" class=\"check\" name=\"ids\" value=\"" + getCreditId(data.CustomerId) + "\" /></td>");
                row.append("<td>" + custName + "</td>");
                row.append("<td>" + data.AmountExcludingTaxes + "</td>");
                row.append("<td>" + data.BonusAmount + "</td>");
                row.append("<td>" + data.Amount + "</td>");

                row.appendTo(tableBody);
                i++;
            }


            tableBody.find("input, a").click(function(event){ //Stop clicks from falling through to the table row event
                event.stopPropagation();
                return true;
            });
            tableBody.find("tr").click(function(event){
                var row = jQuery(this);
                if (row.hasClass("selected")) { //Deselect
                    jQuery("#customerFilter").val(0);
                } else {
                    jQuery("#customerFilter").val(jQuery(this).attr('rel'));
                }
                jQuery("#customerFilter").triggerHandler("change");
            });



            createPager(msg.Pages, jQuery("#pager"));
            jQuery(".checkAll").triggerHandler('click');

            fadeIn(table.find('thead > tr > th'));
        }
    });
}

function downloadListAsCsv() {
    window.location.href = ajaxPath + "?month=" + selMonth + "&year=" + selYear + "&custType=" + customerType + "&sort=" + sort + "&sdir=" + sdir + "&page=0&asCsv=true";
}

function doShowNotes(){
    jQuery.ajax({
        type: "GET",
        url: notesPath + "/" + noteId,
        success: function(msg) {
            jQuery("#tableContent").hide();
            jQuery("#notesContent").html(msg).show();

        }

    });
}

function showNotes(id) {
    noteId = id;
    iqSetHistory();
}

function showTable() {
    noteId = false;
    iqSetHistory();
}

function getCreditId(custId) {
    return selYear + "-" + selMonth + "-" + custId;
}

function sortDataTable(col) {
    if (col == sort) {
        sdir = !sdir;
    } else {
        sdir = false;
    }

    page = 1

    sort = col;
    iqSetHistory();
}

function createPager(totalPages, elm) {
    elm.empty();
    if (totalPages > 1)
        {
            var builder = "";


            var numDirections = 2;

            if (page > 1)
            {
                if (page - numDirections - 1 > 0)
                {
                    builder += CreatePageLinkStatic(1, "&laquo;");
                    builder += " ";
                }

                builder += CreatePageLinkStatic(page - 1, "&lt;");
                builder += " ";
            }

            var n = page - numDirections;
            while (n < page)
            {
                if (n > 0)
                {
                    builder += CreatePageLinkStatic(n, n);
                    builder += " ";
                }
                n++;
            }

            builder += page;
            builder += " ";

            n = page + 1;

            while (n <= page + numDirections && n <= totalPages)
            {
                builder += CreatePageLinkStatic(n, n);
                builder +=" ";
                n++;
            }

            if (page < totalPages)
            {
                builder += CreatePageLinkStatic(page + 1, "&gt;");
                builder += " ";
                if (page + numDirections < totalPages)
                {
                    builder += CreatePageLinkStatic(totalPages, "&raquo;");
                }
            }

            builder;

            elm.append(builder);
        }

}
function CreatePageLinkStatic(page, str){
    return "<a href=\"javascript:pageDataTable(" + page + ")\">" + str + "</a>";
}

function pageDataTable(newPage){
    page = newPage;
    iqSetHistory();
}

そしてマークアップ:

<div id="tableContent">
<select id="customerTypeChanger">
    <option selected="selected" value="Publisher">Publisher</option>
    <option value="Advertiser">Advertiser</option>
</select>
<select id="customerFilter"><option value="0">Choose Customer</option><option value="1">Customer 1</option><option value="1">Customer 2</option>...</select>
<div id="monthPicker"></div>
<div><a href="javascript:downloadListAsCsv()">DownloadAsCSV</a></div>
    <table id="dataTable" class="grid">
        <thead>
            <tr>
                <th style="text-align: left"><input type="checkbox" name="toggleCheckBox" class="checkAll" value="dummy" /></th>
                <th><a href="javascript:sortDataTable('CustomerName')">Customer name</a></th>
                <th><a href="javascript:sortDataTable('AmountExcludingTaxes')">Amount</th>
                <th><a href="javascript:sortDataTable('BonusAmount')">Bonus amount</a></th>
                <th><a href="javascript:sortDataTable('Amount')">Amount including VAT</a></th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
    <div class="pagination" id="pager"></div>
    <div>With the selected rows</div>
<input id="createNotes" type="button" value="Create notes" onclick="javascript:createNotes(this)" /> <input id="deleteNotes" value="Delete notes" type="submit" onclick="javascript:deleteNotes(this)" />

</div>
<div id="notesContent"></div>

必要に応じて、monthpicker 用に作成したコードを以下に示します (これは非常に基本的な datepicker のもので、月を前後にフリックして次のような出力を与えるものです)

< 2009 年 4 月 2009 年 5 月 2009 年 6 月 >

(太字はその月の期間のみを表示するクリック可能なリンク、斜体は既に選択されているもので、明らかに実際の HTML マークアップは異なります)

jQuery UI の datepicker を利用して、ローカライズされた月の名前を取得します。

(function($) {


    var selDate;

    $.fn.monthPicker = function(callback, selectedDate) {

        selDate = selectedDate;

        var elm = this;

        this.html("<span class=\"prevMonthButton\"><a href=\"\">&lt;</a></span><span class=\"prevMonth\"><a href=\"\"></a></span><span class=\"curMonth\"></span><span class=\"nextMonth\"><a href=\"\"></a></span><span class=\"nextMonthButton\"><a href=\"\">&gt;</a></span>");

        populateDates(this);

        var prevMonthFunc = function() {

            var month = selDate.getMonth() - 1;
            if (month < 0) {
                month = 11;
                selDate.setFullYear(selDate.getFullYear() - 1);
            }
            selDate.setMonth(month);

            populateDates(elm);
            callback(selDate.getFullYear(), selDate.getMonth() + 1);
            return false;
        }
        var nextMonthFunc = function() {
            var month = selDate.getMonth() + 1;
            if (month > 11) {
                month = 0;
                selDate.setFullYear(selDate.getFullYear() + 1);
            }
            selDate.setMonth(month);

            populateDates(elm);
            callback(selDate.getFullYear(), selDate.getMonth() + 1);
            return false;
        };

        this.find(".prevMonth > a").click(prevMonthFunc);
        this.find(".prevMonthButton > a").click(prevMonthFunc);
        this.find(".nextMonth > a").click(nextMonthFunc);
        this.find(".nextMonthButton > a").click(nextMonthFunc);
    }

    function populateDates(elm) {

        var months = jQuery.datepicker._defaults.monthNames;
        var selYear = selDate.getFullYear();
        var selMonth = selDate.getMonth();


        elm.find(".curMonth").text(months[selMonth] + " " + selYear);

        var prevMonth = selMonth - 1;
        var prevYear = selYear;
        if (prevMonth < 0) {
            prevMonth = 11;
            prevYear = prevYear - 1;
        }

        elm.find(".prevMonth > a").text(months[prevMonth] + " " + prevYear);

        var nextMonth = selMonth + 1;
        var nextYear = selYear;
        if (nextMonth > 11) {
            nextMonth = 0;
            nextYear = nextYear + 1;
        }

        elm.find(".nextMonth > a").text(months[nextMonth] + " " + nextYear);
    }
})(jQuery);

この JavaScript コードの大部分がひどいことはわかっていますが、主要な部分では非常にうまく機能しているように見えますが、前述したように、行をクリックして選択し、クリックして選択を解除してブーイングし、add_navigate を 2 回呼び出して、 JSON サービスへの余分な呼び出しと、クライアント側で視覚的なちらつきが発生します。なぜそれが起こるのか理解できません (さらに奇妙なのは、なぜ選択されていないときにのみ発生し、選択されたサービスでは発生しないのか) ことです。

役に立ちましたか?

解決

やってみます

.unbind('click').click(function()

の代わりに

.click(function()

クリック イベントが 2 回バインドされないようにするためです。

他のヒント

問題は、同じ要素へのイベントの二重バインディングが無意識に行われていることだと思います。

通常はこれがシナリオですが、

(function($){
   var MyDocument = new Object({
       prepareBody : function(){

          //addClick Event
          $('div#updatedElement').click(MyDocument.ajaxCall());

          //adjusting the height of an updatedElement to an-otherElement
          $('div#updatedElement').css('height', $('div#otherElement').height());
       },
       ajaxCall : function(){

          //do your ajax Call
           $.getJSON('index.php',{param:1, param:2},function(response){

                //do something with your response
                $('div#updatedElement').html(response)

               //say if after your call you decide to update the body again
                MyDocument.prepareBody();

               //what that does is you will double bind click to the updateElement div.
               //The next time that it is click, the AjaxCall function will run twice
                //The next time it is clicked the MyDocument.ajaxCall function will be run four times

                //8 - 16 - 32 and by now, firefox would have crashed!

           },'json');
       }
   });
   $(document).ready(function(){
       MyDocument.prepareBody()
   });
})(jQuery);

したがって、KClough 氏のアドバイスに従って、イベントが 1 回だけ実行されるように、イベントをバインドする前にバインドを解除してください。悲しいことに、jQuery は他のフレームワークのようにそれらを上書きしません。

これが他の人に役立つことを願っています

ああ、ごめんなさい、この質問をすっかり忘れていました。

この後少ししてコードの一部を再構築したところ、問題は解消されました。何が原因だったのかは正確にはわかりませんが、二重ロードが半分の時間しか起こらなかったことに今でも驚いています。

とにかく、問題は解決しましたが、方法や理由がわからないだけで、まだ少し気になりますが、一方で、それは機能します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top