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

28
我有一个带有某些选择字段的页面。 一旦用户更改了选择字段的值,将使用所有选择字段的值作为状态对象创建新的历史记录对象:
$(".chosen-select-field").chosen().change(function() {
    $(".chosen-select-field").each( function ( index ) {
        parameterNames[index].value = $( this ).val();
    });
    history.pushState(parameterNames, '', newUrl);
});

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

parameterNames.push({key:"name", value:"foobar"});
以下代码在用户单击浏览器的“后退”或“前进”按钮时还原状态。它运行正常,但表现出意外行为。
例如,我更改了三个选择字段(创建三个历史记录条目)。然后我返回。执行restoreState,相应地更改一个选择字段。但是浏览器本身仍停留在同一位置(无法向前移动,后面的历史记录条目数量仍相同)。
然后我再次单击“后退”按钮。这次状态对象与上次单击时提供的对象相同。但浏览器向后移动了一个历史记录条目。
第三次单击“后退”按钮时,下一个选择字段会更改,但是浏览器仍停留在该状态,除非我再次单击“后退”按钮。
有人能解释一下我做错了什么吗?
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);

3
当触发popstate事件(例如浏览器后退时),你调用了"restoreState"方法,该方法循环遍历字段并可能调用".change()"方法,该方法再次使用history.pushState()。我建议在popstate事件触发时使用replaceState方法。 - George
https://dev59.com/aXnZa4cB1Zd3GeqPqXm9#20157851 - Tim Vermaelen
2个回答

1
当触发popstate事件并循环遍历选择字段时,如果满足条件$(this).val() != parameterNames[index].value,当前选择的值将被修改,但也会触发change事件,进而再次调用将新状态推入历史记录的函数。这样,如果您回到历史记录中,将获得相同的状态对象。
因此,解决方案是检查是否触发了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);

1
我猜您在到达当前状态时会推送该状态,而不是在到达当前状态时推送上一个状态。这就是为什么当您第一次按返回按钮时,会弹出当前状态,而您需要弹出上一个状态并返回那里的原因...

let boxes = Array.from(document.getElementsByClassName('box'));

function selectBox(id) {
  boxes.forEach(b => {
    b.classList.toggle('selected', b.id === id);
  });
}

boxes.forEach(b => {
  let id = b.id;
  b.addEventListener('click', e => {
    history.pushState({
      id
    }, `Selected: ${id}`, `./selected=${id}`)
    selectBox(id);
  });
});

window.addEventListener('popstate', e => {
  selectBox(e.state.id);
});

history.replaceState({
  id: null
}, 'Default state', './');
.boxes {
  display: flex;
}

.desc {
  width: 100%;
  display: flex;
  align-items: center;
  padding: 3rem 0;
  justify-content: center;
  font-weight: bold;
}

.box {
  --box-color: black;
  width: 50px;
  height: 50px;
  margin: 20px;
  box-sizing: border-box;
  display: block;
  border-radius: 2px;
  cursor: pointer;
  color: white;
  background-color: var(--box-color);
  border: 5px solid var(--box-color);
  font-size: 14px;
  font-family: sans-serif;
  font-weight: bold;
  text-align: center;
  line-height: 20px;
  transition: all 0.2s ease-out;
}

.box:hover {
  background-color: transparent;
  color: black;
}

.box.selected {
  transform: scale(1.2);
}

#box-1 {
  --box-color: red;
}

#box-2 {
  --box-color: green;
}

#box-3 {
  --box-color: blue;
}

#box-4 {
  --box-color: black;
}
<div class="boxes">
  <div class="box" id="box-1">one</div>
  <div class="box" id="box-2">two</div>
  <div class="box" id="box-3">three</div>
  <div class="box" id="box-4">four</div>
</div>
<div class="desc">
  Click to select Each box and u can navigate back using browser back button
</div>

请查看此 Codepen:https://codepen.io/AmalNandan/pen/VwmrQJM?editors=1100


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接