在具有固定顶级菜单和锚点导航的单页面布局上,我有一个" scrollspy"在滚动时更改片段标识符的位置,根据滚动位置为菜单链接提供活动类,并使用Velocity.js动画滚动到锚点。
不幸的是,当它点击浏览器后退按钮时,它会带我完成滚动方式的所有步骤,这意味着我加载网站并向下滚动一点点,然后经常点击后退按钮浏览器将也可以向下和向上滚动,但实际上不会转到上次访问过的ID或浏览器历史记录中。
// jQuery on DOM ready
// In-Page Scroll Animation with VelocityJS
// ------------------------------------------------ //
// https://john-dugan.com/fixed-headers-with-hash-links/
$('.menu-a').on('click', function(e) {
var hash = this.hash,
$hash = $(hash)
addHash = function() {
window.location.hash = hash;
};
$hash.velocity("scroll", { duration: 700, easing: [ .4, .21, .35, 1 ], complete: addHash });
e.preventDefault();
});
// ScrollSpy for Menu items and Fragment Identifier
// ------------------------------------------------ //
// https://jsfiddle.net/mekwall/up4nu/
$menuLink = $('.menu-a')
var lastId,
// Anchors corresponding to menu items
scrollItems = $menuLink.map(function(){
var item = $($(this).attr("href"));
if (item.length) { return item; }
});
$(window).scroll(function(){
// Get container scroll position
var fromTop = $(this).scrollTop()+ 30; // or the value for the #navigation height
// Get id of current scroll item
var cur = scrollItems.map(function(){
if ($(this).offset().top < fromTop)
return this;
});
// Get the id of the current element
cur = cur[cur.length-1];
var id = cur && cur.length ? cur[0].id : "";
if (lastId !== id) {
lastId = id;
// Set/remove active class
$menuLink
.parent().removeClass("active")
.end().filter("[href='#"+id+"']").parent().addClass("active");
}
// If supported by the browser we can also update the URL
// http://codepen.io/grayghostvisuals/pen/EtdwL
if (window.history && window.history.pushState) {
history.pushState("", document.title,'#'+id);
}
});
使用上面的代码,以下工作正常:
使用VelocityJS在滚动动画中单击菜单链接时,哈希或片段标识符更新正常。
活动课程在滚动时会显示相应的菜单链接。
滚动时片段标识符也会更新,而不是点击菜单链接。
问题
第1部分:当您在小提琴上滚动一点点然后点击后退按钮时,您会看到滚动条&#34;旅行&#34;完全相同的方式,记住已完成的滚动。
我需要后退按钮才能正常工作。 a)返回浏览器历史记录并返回到您所在的页面/站点 b)当点击锚链接(i)然后锚定链接(ii)然后返回后退按钮时,页面应该返回到锚点链接(i)。
第2部分:由于IE8不支持history.pushState
,我正在寻找使用window.location.hash = $(this).attr('id');
的方法。无论我在代码结束时尝试过什么,我都无法让window.location.hash = $(this).attr('id');
工作。我真的不想使用HistoryJS或其他东西,但我有兴趣学习这些并自己编写。
除了后退按钮损坏的行为,我想要的所有其他行为已经存在,现在我只需要修复后退按钮的行为。
修改 我想我可能已经找到了解决方案here,我会测试然后详细回复,如果我让它工作的话。
相关:
smooth scrolling and get back button with popState on Firefox - need to click twice
jQuery in page back button scrolling
Modifying document.location.hash without page scrolling
答案 0 :(得分:2)
要回答问题的第一部分,如果您不想污染浏览器的历史记录,可以使用history.replaceState()
代替history.pushState()
。虽然pushState
更改了网址并在浏览器的历史记录中添加了新条目,但replaceState
会在修改当前历史记录条目时更改网址,而不是添加新条目。
还有一篇好文章,包括pushState
和replaceState
on MDN之间的差异。
答案 1 :(得分:1)
对于较旧的浏览器,我决定包含https://github.com/devote/HTML5-History-API,并且在此基础上我获得了所需的行为(或多或少)。
这个答案有:
- 用于菜单项和集的滚动间谍以及用于滚动的活动类
- 滚动间谍也适用于URL哈希,根据当前滚动到的部分设置正确的哈希值
- 滚动停止功能,用于检查用户何时停止滚动,然后从当前活动菜单项中获取值,并将其设置为当前URL哈希。这样做的目的是为了不抓住这些部分。滚动时的锚点,但只有用户滚动到的部分的锚点
- 单击菜单链接时以及使用后退和前进按钮时,使用Velocity.js进行平滑滚动
- 对加载和重新加载页面做出反应的函数,这意味着如果您为某个部分输入具有特定URL哈希的页面,它将为该部分设置滚动动画,如果页面重新加载,则会将滚动设置为当前顶部的动画。部分
代码是粗略的草图,可能会使用一些调整,这只是为了演示目的。我认为我仍然是请指出明显的错误的初学者,以便我可以从中学习。还包括所有指向代码片段来源的链接。
// In-Page Scroll Animation to Menu Link on click
// ------------------------------------------------ //
// https://john-dugan.com/fixed-headers-with-hash-links/
// https://stackoverflow.com/questions/8355673/jquery-how-to-scroll-an-anchor-to-the-top-of-the-page-when-clicked
// http://stackoverflow.com/a/8579673/1010918
// $('a[href^="#"]').on('click', function(e) {
$('.menu-a').on('click', function(e) {
// default = make hash appear right after click
// not default = make hash appear after scrolling animation is finished
e.preventDefault();
var hash = this.hash,
$hash = $(hash)
$hash.velocity("scroll", { duration: 700, easing: [ .4, .21, .35, 1 ], queue: false });
});
// In-Page Scroll Animation to Hash on Load and Reload
// ----------------------------------------------------------- //
// https://stackoverflow.com/questions/680785/on-window-location-hash-change
// hashchange triggers popstate !
$(window).on('load', function(e) {
var hash = window.location.hash;
console.log('hash on window load '+hash);
$hash = $(hash)
$hash.velocity("scroll", { duration: 500, easing: [ .4, .21, .35, 1 ], queue: false });
// if not URL hash is present = root, go to top of page on reload
if (!window.location.hash){
$('body').velocity("scroll", { duration: 500, easing: [ .4, .21, .35, 1 ], queue: false });
}
});
// In-Page Scroll Animation to Hash on Back or Forward Button
// ---------------------------------------------------------- //
$('.menu-a').on('click', function(e) {
e.preventDefault();
// keep the link in the browser history
// set this separately from scrolling animation so it works in IE8
history.pushState(null, null, this.href);
return false
});
$(window).on('popstate', function(e) {
// alert('popstate fired');
$('body').append('<div class="console1">popstate fired</div>');
$('.console1').delay(1000).fadeOut('slow');
if (!window.location.hash){
$('body').append('<div class="console2">no window location hash present</div>');
$('body').velocity("scroll", { duration: 700, easing: [ .4, .21, .35, 1 ], queue: false });
$('.console2').delay(1000).fadeOut('slow');
}
console.log('window.location.hash = '+window.location.hash);
var hash = window.location.hash;
$hash = $(hash)
$hash.velocity("scroll", { duration: 700, easing: [ .4, .21, .35, 1 ], queue: false });
});
// ScrollSpy for Menu items only - gives selected Menu items the active class
// ------------------------------------------------------------------------ //
// Does not update fragment identifier in URL https://en.wikipedia.org/wiki/Fragment_identifier
// https://jsfiddle.net/mekwall/up4nu/
var lastId,
// Anchors corresponding to menu items
scrollItems = $menuLink.map(function(){
var item = $($(this).attr("href"));
// console.table(item);
if (item.length) { return item; }
});
// Give menu item the active class on load of corresponding item
function scrollSpy () {
// Get container scroll position
var fromTop = $(this).scrollTop()+ $menuButtonHeight;
// Get id of current scroll item
var cur = scrollItems.map(function(){
if ($(this).offset().top < fromTop)
return this;
});
// Get the id of the current element
cur = cur[cur.length - 1];
var id = cur && cur.length ? cur[0].id : "";
if (lastId !== id) {
lastId = id;
// Set/remove active class
$menuLink
.parent().removeClass("active").end()
.filter("[href='#"+id+"']").parent().addClass("active");
}
// Active Menu Link href Attribute
activeMenuLinkHref = $('.menu-li.active > .menu-a').attr('href');
// console.log('activeMenuLinkHref '+activeMenuLinkHref);
}
scrollSpy()
$(window).scroll(function(e){
scrollSpy()
});
// On Stop of Scrolling get Active Menu Link Href and set window.location.hash
// --------------------------------------------------------------------------- //
// Scroll Stop Function
//---------------------//
// https://stackoverflow.com/questions/8931605/fire-event-after-scrollling-scrollbars-or-mousewheel-with-javascript
// http://stackoverflow.com/a/8931685/1010918
// http://jsfiddle.net/fbSbT/1/
// http://jsfiddle.net/fbSbT/87/
(function(){
var special = jQuery.event.special,
uid1 = 'D' + (+new Date()),
uid2 = 'D' + (+new Date() + 1);
special.scrollstart = {
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
} else {
evt.type = 'scrollstart';
// throws "TypeError: jQuery.event.handle is undefined"
// jQuery.event.handle.apply(_self, _args);
// solution
// http://stackoverflow.com/a/20809936/1010918
// replace jQuery.event.handle.apply with jQuery.event.dispatch.apply
jQuery.event.dispatch.apply(_self, _args);
}
timer = setTimeout( function(){
timer = null;
}, special.scrollstop.latency);
};
jQuery(this).bind('scroll', handler).data(uid1, handler);
},
teardown: function(){
jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) );
}
};
special.scrollstop = {
latency: 250,
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout( function(){
timer = null;
evt.type = 'scrollstop';
// throws "TypeError: jQuery.event.handle is undefined"
// jQuery.event.handle.apply(_self, _args);
// solution
// http://stackoverflow.com/a/20809936/1010918
// replace jQuery.event.handle.apply with jQuery.event.dispatch.apply
jQuery.event.dispatch.apply(_self, _args);
}, special.scrollstop.latency);
};
jQuery(this).bind('scroll', handler).data(uid2, handler);
},
teardown: function() {
jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) );
}
};
})();
// Scroll Stop Function Called
//----------------------------//
$(window).on("scrollstop", function() {
// window.history.pushState(null, null, activeMenuLinkHref);
// window.history.replaceState(null, null, activeMenuLinkHref);
// http://stackoverflow.com/a/1489802/1010918 //
// works best really
hash = activeMenuLinkHref.replace( /^#/, '' );
console.log('hash '+hash);
var node = $( '#' + hash );
if ( node.length ) {
node.attr( 'id', '' );
// console.log('node.attr id'+node.attr( 'id', '' ));
}
document.location.hash = hash;
if ( node.length ) {
node.attr( 'id', hash );
}
});
CSS
.console1{
position: fixed;
z-index: 9999;
top:0;
right:0;
background-color: #fff;
border: 2px solid red;
}
.console2{
position: fixed;
z-index: 9999;
bottom:0;
right:0;
background-color: #fff;
border: 2px solid red;
}
我也会在适当的时候提供一个小伙伴。 ;)