Javascript 更改 window.location.hash 而不滚动,但仍会触发 CSS :target 伪类



据我所知,有两种方法可以更改URL末尾的哈希。window.location.hashhistory.pushState.但是,history.pushState不会触发 CSS:target伪类,因此它就出来了。

"在不滚动页面的情况下修改 document.location.hash"的答案演示了一种在不滚动的情况下修改location.hash的方法,但此解决方法无法触发具有匹配 ID 的元素上的:target伪类。

下面是一个简单的例子。两个链接按预期工作:触发:target并显示选项卡,但如有必要,它们也会滚动到视图中。两个链接不起作用::target未触发,但阻止滚动。

(function() {
function setHash(event) {
event.preventDefault();
var decoy = document.querySelector(".dummy-tabpanel");
var tabId = event.currentTarget.getAttribute('href');
var id = tabId.replace( /^#/, '' );
var tabPanel = document.getElementById(id);

decoy.style.top = document.body.scrollTop + 'px';

tabPanel.setAttribute('id', '');
decoy.setAttribute('id', id);

window.location.hash = tabId;

decoy.setAttribute('id', '');
tabPanel.setAttribute('id', id);

return false;
}

function setHashDirectly(event) {
event.preventDefault();
var tabId = event.currentTarget.getAttribute('href');
window.location.hash = tabId;
return false;
}

var tabLinks = document.querySelectorAll('a[href^="#"]');

for (var tabLink of tabLinks) {
tabLink.addEventListener("click", tabLink.classList.contains('direct') ? setHashDirectly : setHash );
}
})()
.tabs {
margin-top: 300px; // attempt to make scrolling necessary
}
.tabs [role="tabpanel"] {
display: none;
}
.tabs [role="tabpanel"]:target {
display: block;
}
.dummy-tabpanel {
position: absolute;
visibility: hidden;
}
<nav>
<ul>
<li>
<a href="#one">one (doesn't scroll or trigger <code>:target</code>)</a>
</li>
<li>
<a href="#two">two (doesn't scroll or trigger <code>:target</code>)</a>
</li>
<li>
<a href="#three" class="direct">three (triggers <code>:target</code> but scrolls)</a>
</li>
<li>
<a href="#four" class="direct">four (triggers <code>:target</code> but scrolls)</a>
</li>
</ul>
</nav>
<div class="tabs">
<div role="tabpanel" id="one">
this is the first tab (won't display)
</div>
<div role="tabpanel" id="two">
this is the two tab (won't display)
</div>
<div role="tabpanel" id="three">
this is the third tab (should display)
</div>
<div role="tabpanel" id="four">
this is the forth tab (should display)
</div>
</div>
<div class="dummy-tabpanel"></div>

有没有办法获得"两全其美",即在不滚动页面的情况下更改窗口哈希触发 CSS:target

注意:我已经在Chrome 64和OS X 10.13上的Firefox 58上对此进行了测试。

实际上,history.pushState可能应该更新 CSS:target选择器,因为在使用history.pushState后使用浏览器后退和前进按钮来回导航确实会更新它。它在webkit中有一个开放的错误,并且有人谈论将行为标准化为一种或另一种方式,而不是当前的不一致行为。如果他们选择让它更新 CSS,那么你的代码将按原样工作。如果他们选择不更新CSS,那么浏览器将不得不更改后退和前进按钮的行为,以不更新history.pushState创建的条目的CSS,或者可能的任何条目,这似乎是一个明显的错误举动。

因此,您的代码将来可能会在不更改的情况下工作,但到目前为止,您必须通过调用history.pushState然后为用户来回导航来解决此问题。以下是基于 GitHub 上 laughinghan 的解决方案的解决方案:

function pushHashAndFixTargetSelector(hash) {
history.pushState({}, document.title, hash); //called as you would normally
const onpopstate = window.onpopstate; //store the old event handler to restore it later
window.onpopstate = function() { //this will be called when we call history.back()
window.onpopstate = onpopstate; //restore the original handler
history.forward(); //go forward again to update the CSS
};
history.back(); //go back to trigger the above function
}

从理论上讲,即使在标准化预期行为之后,此解决方法仍将继续工作。

最新更新