在Chrome中加载页面时popup窗口
我正在使用我的Web应用程序的历史API,并有一个问题。 我做Ajax调用来更新页面上的一些结果,并使用history.pushState()来更新浏览器的地址栏,而无需重新加载页面。 然后,当然,我使用window.popstate为了恢复以前的状态,当单击后退button。
这个问题是众所周知的 – Chrome和Firefox将popstate事件视为不同的方式。 尽pipeFirefox在第一次加载时没有启动,但Chrome仍在运行。 我想拥有Firefox风格,并且不会在加载时触发事件,因为它只是在加载时使用完全相同的结果更新结果。 有除了使用History.js的解决方法吗? 我不想使用它的原因是 – 它本身需要太多的JS库,因为我需要在已经有太多JS的CMS中实现它,所以我想尽量减lessJS 。
因此,想知道是否有一种方法可以使Chrome不会在加载时popup“popstate”,或者有人试图使用History.js,因为所有的库都被合并成一个文件。
在版本19的Google Chrome中,@spliter的解决scheme停止工作。 正如@johnnymire指出的那样,Chrome 19中的history.state存在,但是它是空的。
我的解决方法是将window.history.state!== null添加到检查window.history中是否存在状态:
var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href;
我在所有主stream浏览器和Chrome 19和18版本中testing了它。它看起来像是有效的。
如果您不想为每个处理程序采取特殊措施,您可以将其添加到onpopstate,我的解决scheme可能会对您有所帮助。 这个解决scheme的另外一个好处是onpopstate事件可以在页面加载完成之前处理。
在添加任何onpopstate处理程序之前,只需运行一次该代码,一切都应按预期工作(又如在Mozilla中^^) 。
(function() { // There's nothing to do for older browsers ;) if (!window.addEventListener) return; var blockPopstateEvent = document.readyState!="complete"; window.addEventListener("load", function() { // The timeout ensures that popstate-events will be unblocked right // after the load event occured, but not in the same event-loop cycle. setTimeout(function(){ blockPopstateEvent = false; }, 0); }, false); window.addEventListener("popstate", function(evt) { if (blockPopstateEvent && document.readyState=="complete") { evt.preventDefault(); evt.stopImmediatePropagation(); } }, false); })();
怎么运行的:
Chrome浏览器 ,Safari浏览器以及其他可能的webkit浏览器会在文档加载完成时触发onpopstate事件。 这不是意图,所以我们阻止popstate事件,直到加载文档之后的第一个事件循环cicle。 这是通过preventDefault和stopImmediatePropagation调用完成的(与stopPropagation不同,stopImmediatePropagation立即停止所有的事件处理调用)。
然而,由于当Chrome浏览器错误地激活poppop时,文档的readyState已经处于“完成”状态,所以我们允许opopstate事件在文档加载完成之前触发,以便在文档加载之前允许onpopstate调用。
更新 2014年4月23日:修复了在加载页面后执行脚本的情况下popstate事件被阻止的问题。
仅使用setTimeout并不是一个正确的解决scheme,因为您不知道要加载内容需要多长时间,因此可能会在超时后发出popstate事件。
这是我的解决scheme: https : //gist.github.com/3551566
/* * Necessary hack because WebKit fires a popstate event on document load * https://code.google.com/p/chromium/issues/detail?id=63040 * https://bugs.webkit.org/process_bug.cgi */ window.addEventListener('load', function() { setTimeout(function() { window.addEventListener('popstate', function() { ... }); }, 0); });
解决方法已经在jquery.pjax.js行195-225:
// Used to detect initial (useless) popstate. // If history.state exists, assume browser isn't going to fire initial popstate. var popped = ('state' in window.history), initialURL = location.href // popstate handler takes care of the back and forward buttons // // You probably shouldn't use pjax on pages with other pushState // stuff yet. $(window).bind('popstate', function(event){ // Ignore inital popstate that some browsers fire on page load var initialPop = !popped && location.href == initialURL popped = true if ( initialPop ) return var state = event.state if ( state && state.pjax ) { var container = state.pjax if ( $(container+'').length ) $.pjax({ url: state.url || location.href, fragment: state.fragment, container: container, push: false, timeout: state.timeout }) else window.location = location.href } })
比重新实现pjax更直接的解决scheme是在pushState上设置一个variables,并检查popState上的variables,所以初始的popState不会在加载时不一致地触发(而不是jQuery特定的解决scheme,只是将它用于事件):
$(window).bind('popstate', function (ev){ if (!window.history.ready && !ev.originalEvent.state) return; // workaround for popstate on load }); // ... later ... function doNavigation(nextPageId) { window.history.ready = true; history.pushState(state, null, 'content.php?id='+ nextPageId); // ajax in content instead of loading server-side }
Webkit的初始onpopstate事件没有分配状态,所以你可以用它来检查不需要的行为:
window.onpopstate = function(e){ if(e.state) //do something };
一个全面的解决scheme,允许导航回到原来的页面,将build立在这个想法:
<body onload="init()"> <a href="page1" onclick="doClick(this); return false;">page 1</a> <a href="page2" onclick="doClick(this); return false;">page 2</a> <div id="content"></div> </body> <script> function init(){ openURL(window.location.href); } function doClick(e){ if(window.history.pushState) openURL(e.getAttribute('href'), true); else window.open(e.getAttribute('href'), '_self'); } function openURL(href, push){ document.getElementById('content').innerHTML = href + ': ' + (push ? 'user' : 'browser'); if(window.history.pushState){ if(push) window.history.pushState({href: href}, 'your page title', href); else window.history.replaceState({href: href}, 'your page title', href); } } window.onpopstate = function(e){ if(e.state) openURL(e.state.href); }; </script>
虽然这仍然可以触发两次(有一些漂亮的导航),但可以简单地通过检查前一个href来进行处理。
这是我的解决方法。
window.setTimeout(function() { window.addEventListener('popstate', function() { // ... }); }, 1000);
这是我的解决scheme:
var _firstload = true; $(function(){ window.onpopstate = function(event){ var state = event.state; if(_firstload && !state){ _firstload = false; } else if(state){ _firstload = false; // you should pass state.some_data to another function here alert('state was changed! back/forward button was pressed!'); } else{ _firstload = false; // you should inform some function that the original state returned alert('you returned back to the original state (the home state)'); } } })
让Chrome浏览器在网页加载时不触发popstate的最佳方式是向https://code.google.com/p/chromium/issues/detail?id=63040投票。; 他们已经知道Chrome浏览器已经不符合HTML5规范了两年,现在仍然没有修复!
如果使用event.state !== null
返回历史logging到第一个加载的页面将无法在非移动浏览器中工作。 我使用sessionStorage来标记何时ajax导航真的开始。
history.pushState(url, null, url); sessionStorage.ajNavStarted = true; window.addEventListener('popstate', function(e) { if (sessionStorage.ajNavStarted) { location.href = (e.state === null) ? location.href : e.state; } }, false);
提出的解决scheme在重新加载页面时遇到问题。 以下似乎更好,但我只testing了Firefox和Chrome。 它使用的实际,似乎有e.event.state
和window.history.state
之间的差异。
window.addEvent('popstate', function(e) { if(e.event.state) { window.location.reload(); // Event code } });
我知道你反对,但是你应该使用History.js,因为它消除了一百万浏览器的不兼容性。 我只是去了手动修复路线,以后才发现越来越多的问题只能在路上find。 现在真的不是那么难:
<script src="ajax/libs/history.js/1.8/native.history.min.js" type="text/javascript"></script>
这解决了我的问题。 我所做的只是设置了一个超时函数,它延迟了函数的执行时间,以致错过页面载入的popstate事件
if (history && history.pushState) { setTimeout(function(){ $(window).bind("popstate", function() { $.getScript(location.href); }); },3000); }
您可以创build一个事件并在onload处理程序之后触发它。
var evt = document.createEvent("PopStateEvent"); evt.initPopStateEvent("popstate", false, false, { .. state object ..}); window.dispatchEvent(evt);
请注意,这在Chrome / Safari中略有中断,但是我已将修补程序提交到WebKit,并且应该很快可用,但它是“最正确的”方法。
这在Firefox和Chrome中适用于我
window.onpopstate = function(event) { //back button click console.log("onpopstate"); if (event.state) { window.location.reload(); } };