为什么在测试ui.sender时jquery sortable中的更新事件似乎运行了两次

为什么在测试ui.sender时jquery sortable中的更新事件似乎运行了两次,jquery,jquery-ui-sortable,Jquery,Jquery Ui Sortable,我正在使用jQueryUISortable对连接的列表进行排序。更新事件似乎运行了两次 以下是完整的可排序呼叫: $(".pageContent").sortable({ handle: ".quesText", connectWith: ".pageContent", containment: "section", start: function(e, ui){ ui.placeholder.height(ui.item.height());

我正在使用jQueryUISortable对连接的列表进行排序。更新事件似乎运行了两次

以下是完整的可排序呼叫:

$(".pageContent").sortable({
    handle: ".quesText",
    connectWith: ".pageContent",
    containment: "section",
    start: function(e, ui){
        ui.placeholder.height(ui.item.height());
    },
    placeholder: "sortable-placeholder",
    opacity: 0.5,
    cursor: "move",
    cancel: "input, select, button, a, .openClose",
    update: function(e, ui){
        var thisItem = ui.item;
        var next = ui.item.next();
        var prev = ui.item.prev();

        var thisNumber = thisItem.find(".quesNumb");
        var nextNumber = next.find(".quesNumb");
        var prevNumber = prev.find(".quesNumb");

        var tn = parseInt(thisNumber.text());
        var nn = parseInt(nextNumber.text());
        var pn = parseInt(prevNumber.text());

        var quesNumbs = $(".quesNumb");

        var newItemId = thisItem.attr("id").replace(/_\d+$/, "_");

        //test if we are dragging top down
        if(ui.position.top > ui.originalPosition.top){
            quesNumbs.each(function(i){
                var thisVal = parseInt($(this).text());
                var grandparent = $(this).parent().parent();
                var grandId = grandparent.attr("id").replace(/_\d+$/, "_");
                if(thisVal > tn && (thisVal <= pn || thisVal <= (nn - 1))){
                    $(this).text(thisVal - 1 + ".");
                    grandparent.attr("id",grandId + (thisVal - 1));
                }
            });
            if(!isNaN(pn) || !isNaN(nn)){
                if(!isNaN(pn)){
                    //for some reason when there is a sender pn gets updated, so we check if sender exists
                    //only occurs sorting top down
                    if($.type(ui.sender) !== "null"){
                        var senderId = ui.sender.attr("id");
                        thisNumber.text(pn + 1 + ".");
                        thisItem.attr("id",senderId + "_" + (pn + 1));
                        alert(thisItem.attr("id"));
                    }
                    else {
                        thisNumber.text(pn + ".");
                        thisItem.attr("id",newItemId + pn);
                        alert(thisItem.attr("id"));
                    }
                }
                else {
                    thisNumber.text(nn - 1 + ".");
                }
            }
            else {
                   //something will happen here
            }
        }
        //otherwise we are dragging bottom up
        else {
            quesNumbs.each(function(i){
                var thisVal = parseInt($(this).text());
                if(thisVal < tn && (thisVal >= nn || thisVal >= (pn + 1))){
                    $(this).text(thisVal + 1 + ".");
                }
            });
            if(!isNaN(pn) || !isNaN(nn)){
                if(!isNaN(pn)){
                    thisNumber.text(pn + 1 + ".");
                }
                else {
                    thisNumber.text(nn + ".");
                }
            }
            else {
               //something will happen here
            }
        }
    }
});
我希望只有一个
警报
作为
ui。当排序停留在同一列表中时,发送者
null
。当一个项目离开一个列表转到另一个列表时,
ui.sender
将不再是
null

问题是,当我将一个项目移动到新列表时,我将收到2警报消息。就好像ui.sender是在更新函数运行一次之后设置的,然后它会再次运行更新函数。显然,这是不好的,因为我正在覆盖不一定要覆盖的数据

如果是这样,我应该如何重写代码以避免覆盖数据

编辑

我相信每次更改列表DOM时都会调用更新事件,而不仅仅是在一般情况下更改DOM。因此,对于每个具有DOM更改的列表,更新都会运行。当我将一个项目移动到一个新列表时,我正在更新两个列表


所以我想新的问题是:如果知道这段代码会触发两次,我如何重写它?是否有接收和删除事件的组合可以实现这一点?

我对每个可排序事件进行了一些调查。以下是我的发现,按发生顺序列出:

$(".pageContent").sortable({
    start: function(e,ui){
        //Before all other events
        //Only occurs once each time sorting begins
    },
    activate: function(e,ui){
        //After the start event and before all other events
        //Occurs once for each connected list each time sorting begins
    },
    change: function(e,ui){
        //After start/active but before over/sort/out events
        //Occurs every time the item position changes
        //Does not occur when item is outside of a connected list
    },
    over: function(e,ui){
        //After change but before sort/out events
        //Occurs while the item is hovering over a connected list
    },
    sort: function(e,ui){
        //After over but before out event
        //Occurs during sorting
        //Does not matter if the item is outside of a connected list or not
    },
    out: function(e,ui){
        //This one is unique
        //After sort event before all drop/ending events unless **see NOTE
        //Occurs, only once, the moment an item leaves a connected list
        //NOTE: Occurs again when the item is dropped/sorting stops 
        //--> EVEN IF the item never left the list
        //--> Called just before the stop event but after all other ending events
    },
    beforeStop: function(e,ui){
        //Before all other ending events: update,remove,receive,deactivate,stop
        //Occurs only once at the last possible moment before sorting stops
    },
    remove: function(e,ui){
        //After beforeStop and before all other ending events
        //Occurs only once when an item is removed from a list
    },
    receive: function(e,ui){
        //After remove and before all other ending events
        //Occurs only once when an item is added to a list
    },
    update: function(e,ui){
        //After receive and before all other ending events
        //Occurs when the DOM changes for each connected list
        //This can fire twice because two lists can change (remove from one
        //list but add to another)
    },
    deactivate: function(e,ui){
        //After all other events but before out (kinda) and stop
        //Occurs once for each connected list each time sorting ends
    },
    stop: function(e,ui){
        //After all other events
        //Occurs only once when sorting ends
    }
});

在解决我的问题时,我只是强制更新函数的内容只运行一次,方法是将其包装在检查
ui.sender
if-else
语句中。基本上它说如果
ui.sender
不存在,那么这是第一次使用更新功能,我们应该执行该功能,否则什么也不做。

我在这里发布了答案:

您可以组合remove和receive,并创建一个数组,该数组将保存更改,然后将其作为JSON发布到服务器

演示:

HTML:


第一步
图10
图11
图12
步骤2
图21
图22
图23
JS:

$(函数(){
/*我们将在这里存储所有数据*/
var myArguments={};
函数assembleData(对象、参数)
{       
var data=$(object.sortable('toArray');//获取数组数据
var step_id=$(object.attr(“id”);//获取step_id并将其用作属性名
var arrayLength=data.length;//无需解释
/*如果步骤id属性不存在,请创建该属性*/
if(!arguments.hasOwnProperty(步骤id))
{ 
参数[步骤id]=新数组();
}   
/*循环浏览所有项目*/
对于(变量i=0;i
解决方案的完整说明:

很抱歉,但我认为您的事件顺序中至少存在一个小问题:
停止
不是最后一个事件,而是
更新
。如果我查看as引用来备份我,很明显,
stop
事件发生在排序结束时,而
update
事件发生在排序已结束且DOM已更改时。。。所以
update
将在
stop
之后发生,这样做很合乎逻辑,你不这么认为吗?@chool谢谢你提供了更有意义的信息。但我在这里得到的信息是通过我对每个事件的测试得到的。我通过在触发该事件时调用console.log(“无论该事件是什么”)进行测试。我的发现是更新事件在停止事件之前发送到日志中。源代码从不说谎:-停止事件是最后一个事件非常感谢,我尝试找到了发生这种情况的原因,并最终用您建议的解决方案解决了它。
$(".pageContent").sortable({
    start: function(e,ui){
        //Before all other events
        //Only occurs once each time sorting begins
    },
    activate: function(e,ui){
        //After the start event and before all other events
        //Occurs once for each connected list each time sorting begins
    },
    change: function(e,ui){
        //After start/active but before over/sort/out events
        //Occurs every time the item position changes
        //Does not occur when item is outside of a connected list
    },
    over: function(e,ui){
        //After change but before sort/out events
        //Occurs while the item is hovering over a connected list
    },
    sort: function(e,ui){
        //After over but before out event
        //Occurs during sorting
        //Does not matter if the item is outside of a connected list or not
    },
    out: function(e,ui){
        //This one is unique
        //After sort event before all drop/ending events unless **see NOTE
        //Occurs, only once, the moment an item leaves a connected list
        //NOTE: Occurs again when the item is dropped/sorting stops 
        //--> EVEN IF the item never left the list
        //--> Called just before the stop event but after all other ending events
    },
    beforeStop: function(e,ui){
        //Before all other ending events: update,remove,receive,deactivate,stop
        //Occurs only once at the last possible moment before sorting stops
    },
    remove: function(e,ui){
        //After beforeStop and before all other ending events
        //Occurs only once when an item is removed from a list
    },
    receive: function(e,ui){
        //After remove and before all other ending events
        //Occurs only once when an item is added to a list
    },
    update: function(e,ui){
        //After receive and before all other ending events
        //Occurs when the DOM changes for each connected list
        //This can fire twice because two lists can change (remove from one
        //list but add to another)
    },
    deactivate: function(e,ui){
        //After all other events but before out (kinda) and stop
        //Occurs once for each connected list each time sorting ends
    },
    stop: function(e,ui){
        //After all other events
        //Occurs only once when sorting ends
    }
});
<div class="container">
    <div class="step" id="step_1">
        <h2 class="title">Step 1</h2>
        <div class="image" id="image_10">Image 10</div>
        <div class="image" id="image_11">Image 11</div>
        <div class="image" id="image_12">Image 12</div>
    </div>
    <div class="step" id="step_2">
        <h2 class="title">Step 2</h2>
        <div class="image" id="image_21">Image 21</div>
        <div class="image" id="image_22">Image 22</div>
        <div class="image" id="image_23">Image 23</div>
    </div>
   $(function(){

        /* Here we will store all data */
        var myArguments = {};   

        function assembleData(object,arguments)
        {       
            var data = $(object).sortable('toArray'); // Get array data 
            var step_id = $(object).attr("id"); // Get step_id and we will use it as property name
            var arrayLength = data.length; // no need to explain

            /* Create step_id property if it does not exist */
            if(!arguments.hasOwnProperty(step_id)) 
            { 
                arguments[step_id] = new Array();
            }   

            /* Loop through all items */
            for (var i = 0; i < arrayLength; i++) 
            {
                var image_id = data[i]; 
                /* push all image_id onto property step_id (which is an array) */
                arguments[step_id].push(image_id);          
            }
            return arguments;
        }   

        /* Sort images */
        $('.step').sortable({
            connectWith: '.step',
            items : ':not(.title)',
            /* That's fired first */    
            start : function( event, ui ) {
                myArguments = {}; /* Reset the array*/  
            },      
            /* That's fired second */
            remove : function( event, ui ) {
                /* Get array of items in the list where we removed the item */          
                myArguments = assembleData(this,myArguments);
            },      
            /* That's fired thrird */       
            receive : function( event, ui ) {
                /* Get array of items where we added a new item */  
                myArguments = assembleData(this,myArguments);       
            },
            update: function(e,ui) {
                if (this === ui.item.parent()[0]) {
                     /* In case the change occures in the same container */ 
                     if (ui.sender == null) {
                        myArguments = assembleData(this,myArguments);       
                    } 
                }
            },      
            /* That's fired last */         
            stop : function( event, ui ) {                  
                /* Send JSON to the server */
                $("#result").html("Send JSON to the server:<pre>"+JSON.stringify(myArguments)+"</pre>");        
            },  
        });
    });