需要单击两次才能返回历史记录(使用pushState)

Hed*_*dge 9 javascript jquery html5 browser-history html5-history

我有一个包含一些选择字段的页面.一旦用户更改了选择字段的值.使用所有选择字段的值作为状态对象创建新的历史对象:

$(".chosen-select-field").chosen().change(function() {
    $(".chosen-select-field").each( function ( index ) {
        parameterNames[index].value = $( this ).val();
    });
    history.pushState(parameterNames, '', newUrl);
});
Run Code Online (Sandbox Code Playgroud)

parameterNames是一个包含键和值的对象数组,例如

parameterNames.push({key:"name", value:"foobar"});
Run Code Online (Sandbox Code Playgroud)

当用户单击浏览器中的后退或前进按钮时,以下代码将恢复状态.它工作但行为意外.

例如,我更改了三个选择字段(创建三个历史记录条目).然后我回去 执行restoreState,相应地更改一个选择字段.但是浏览器本身仍然处于历史中的相同位置(无法前进,仍然有相同数量的反向历史条目).

然后我再次点击后退按钮.这次state-object与我点击的列表时间相同.浏览器会移回历史记录中的一个条目.

第三次单击后退按钮时,下一个选择字段已更改,但浏览器再次保持该状态,除非我第四次单击.

谁能解释我做错了什么?

var restoreState = function(event) {
    event = event.originalEvent;
    parameterNames = event.state;
    $(".chosen-select-field").each( function ( index ) {
        if ($( this ).val() != parameterNames[index].value){
            $( this ).val(parameterNames[index].value).change();
            $( this ).trigger('chosen:updated');
        }
    });
};

// Bind history events
$(window).bind("popstate",restoreState);
Run Code Online (Sandbox Code Playgroud)

小智 1

当 popstate 事件被触发并且您循环遍历选择字段时,如果$( this ).val() != parameterNames[index].value满足条件,当前选择值将被更改,但也会触发更改事件,该事件又将再次调用推送新值的函数状态载入历史。这样,如果你回顾历史,你将得到相同的状态对象。

因此,解决方案是检查 popstate 事件是否被触发,如果是,则不要调用history.pushState,而是调用history.replaceState

function handleHistoryStates(popstateEventWasFired = false) {
    $(".chosen-select-field").each( function ( index ) {
        parameterNames[index].value = $( this ).val();
    });
    
    if (popstateEventWasFired) {
        history.replaceState(parameterNames, '', newUrl);
    } else {
        history.pushState(parameterNames, '', newUrl);
    }
}

$(".chosen-select-field").chosen().change(handleHistoryStates);


var restoreState = function(event) {
    event = event.originalEvent;
    parameterNames = event.state;
    $(".chosen-select-field").each( function ( index ) {
        if ($( this ).val() != parameterNames[index].value){
            $( this ).val(parameterNames[index].value);
            handleHistoryStates(true);
            $( this ).trigger('chosen:updated');
        }
    });
};

// Bind history events
$(window).bind("popstate",restoreState);
Run Code Online (Sandbox Code Playgroud)