停止在哈希变化时触发popstate事件。

24
我正在使用历史记录 API 并使用 push 和 pop 状态。我想在某些场景下停止 popstate 事件触发,例如只在单击锚点时将 # 追加到 URL 中并立即触发 popstate。我希望避免所有追加 ##somehasvalue 到 URL 的情况,并停止 popstate 事件的触发。我将查询参数与 URL 一起维护,并且没有任何需要在 URL 中使用 # 触发 popstate 事件的场景。
这是我的代码。
if (supportsHistoryApi()) {

    window.onpopstate = function (event) {
    var d = event.state || {state_param1: param1, state_param2: param2};                       
    var pathName = window.location.pathname,
        params   = window.location.search;

    loadData(event, pathName + params, d.state_param1, d.state_param2);

}
5个回答

19

据我所知,很遗憾您无法阻止popstate的触发。

您可以检查event.state对象。这将在哈希更改时为null。 因此,我建议添加一个。

if(event.state === null) {
   event.preventDefault();
   return false;
}

在您的popstate事件处理程序中加入如下代码,我认为这是防止哈希值更改时触发您方面的popstate处理代码的最佳方法。但如果有其他解决方案,我很感兴趣。


7
当你返回到你打开的第一页时,这种方法就不起作用了。因为此时“state”也为“null”,但仍然没有散列更改。 - Hugo Delsing
3
@HugoDelsing,也许始终在页面加载时使用replaceState - Gust van de Wal
1
顺便提一下,popState事件是不可取消的,因此使用preventDefault无效。但在页面加载时结合调用replaceState并检查null似乎可以正常工作。 - Nikita
我想你是指 history.state。当使用 pushStatereplaceState 时,你可以传递一个自定义值给 history.state - Gonzalo

2

我有一个解决方案!

  • 当popstate事件被调用时,检查路径名是否已更改,或只是哈希值更改。请看下面的演示如何运作:
        window.pop_old = document.location.pathname;
        window.pop_new = '';
        window.onpopstate = function (event) {

            window.pop_new = document.location.pathname;

            if(pop_new != pop_old){

                //diferent path: not just the hash has changed

            } else {

                //same path: just diferent hash

            }

            window.pop_old = pop_new; //save for the next interaction

        };

0

我在SPA中遇到了同样的问题。
我通过检查当前URL和新URL是否相同来解决此问题 - 技术上说我不会阻止popstate,但我会阻止仅哈希值更改时的抓取(fetch)

所以,当页面加载时,我获取当前URL。它必须是变量以全局使用:

var currentURL = window.location.origin + window.location.pathname + window.location.search;

然后当历史更改事件被触发(通过链接点击,点击本地浏览器按钮“后退”“前进”)时,我会检查当前URL和新URL是否相同(忽略哈希)。
如果URL相同,则返回,否则更新当前URL。

let newURL = window.location.origin + window.location.pathname + window.location.search;
if ( currentURL == newURL ) return;
currentURL = newURL;

此外,您可以查看控制器代码。我添加了这个功能,以便在用户快速点击“后退”或“前进”按钮时停止加载多次请求 - 但我只需要加载最后一个请求。
完整解决方案是在URL更改时加载HTML文件(忽略仅哈希更改),并具有推送到历史记录和可用的“后退”和“前进”按钮。
// get current URL when page loaded.
var currentURL = window.location.origin + window.location.pathname + window.location.search;

// function which load html.
window.xhrRequestDoc = ( currentLink, pushToHistory = true, scrollTo = 'body' ) => {
    if ( pushToHistory ) {
        history.pushState( null, null, currentLink );
    }

    // get new URL
    let newURL = window.location.origin + window.location.pathname + window.location.search;

    // return if hash only changed
    if ( currentURL == newURL ) return;

    // update URL
    currentURL = newURL;

    document.body.classList.add( 'main_loading', 'xhr_in_progress' );

    // create controler to stop loading - used when user clicked a few times 'back' or 'forward'.
    controller = new AbortController();
    const signal = controller.signal;

    fetch( currentLink, { signal: signal })
        .then( response => response.text() )
        .then( function( html ) {

            // initialize the DOM parser and parse as html
            let parser = new DOMParser();
            let doc = parser.parseFromString( html, "text/html" );

            // insert data and classes to 'body'
            document.body.innerHTML = doc.querySelector( 'body' ).innerHTML;
        } );
}

window.addEventListener( 'popstate', () => {
    // if user clicked a few times 'back' or 'forward' - process last only
    if ( document.querySelector( '.xhr_in_progress' ) ) controller.abort();

    // run xhr
    xhrRequestDoc( location.href, false );
})

0

这很简单。

为了防止在单击带有哈希的链接后触发popstate事件,您必须消除单击链接的结果 - 即将哈希添加到浏览器地址栏中。

基本上,您必须为click事件创建处理程序,检查单击是否在要防止哈希出现在URL中的元素上,并通过在处理程序中调用event.preventDefault();来防止哈希出现。

以下是代码示例:

/**
 * This your existing `onpopstate` handler.
 */
window.onpopstate = function(e) {
    // You could want to prevent defaut behaviour for `popstate` event too.
    // e.preventDefault(); 
    // Your logic here to do things. 

    // The following reload is just an example of what you could want to make
    // in this event handler.
    window.location.reload();
};

/**
 * This is the `click` event handler that conditionally prevents the default
 * click behaviour.
 */
document.addEventListener('click', function(e) {
    // Here you check if the clicked element is that special link of yours.
    if (e.target.tagName === "A" && e.target.hash.indexOf('#the-link-i-want-make-discernable') > -1) {
        // The 'e.preventDefault()' is what prevent the hash to be added to
        // the URL and therefore prevents your 'popstate' handler to fire.
        e.preventDefault();
        processMySpecialLink(e, e.target);
    }

});

/**
 * Just an example of the link click processor in case you want making something
 * more on the link click (e.g. smooth scroll to the hash).
 */
function processMySpecialLink(e, target) {
    // Make things here you want at user clicking your discernable link.
}

这里是匹配的HTML标记:

<!-- Somewhere in the markup -->
<span id="the-link-i-want-make-discernable"></span>
<!-- Some markup -->
<a href="#the-link-i-want-make-discernable">My Special Link</a>
<!-- Another markup -->
<a href="#just-common-link">Any other link</a>

这一切都做了上面描述的内容:防止特殊哈希链接的默认行为。作为一个副作用,当点击#the-link-i-want-make-discernable哈希链接的特殊情况下,不会添加哈希到URL,也就不会触发popstate事件。


当用户使用“后退”和“前进”本地按钮时,它将无法工作。 - Ivan Frolov

0

如果有人仍然需要这个:

var is_hashed = false;

$(window).on('hashchange', function() {
    is_hashed = true;
});

window.addEventListener('popstate', function(e){
    // if hashchange
    if (is_hashed) {
        e.preventDefault();
        // reset
        is_hashed = false;
        return false;
    }

    // Your code to handle popstate here
    ..................
});

4
我无法使其工作。使用Chrome 85版本。popstate事件会在hashchange之前发生。 - ZalemCitizen

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