如何在长滚动页面上更改 URL?
How do I approach changing URL's on a long scroll page?
我的任务是创建一个长页面,其中包含各种小文章供用户滚动浏览。每个文章部分都有一个类别,我需要 URL 来反映正在查看的类别。我知道 history.pushState()
可用于在触发时更改 URL。但是我不清楚的是物流。
我已经找到了一种方法,可以使用以下代码将主菜单链接跳转到适当的类别,而且似乎工作正常。
$('a.js-link').on('click',function (e) {
e.preventDefault; // dont actually follow the link
var category = $(this).data('category-id'); // get the id we should anchor to
location.hash = '#' + category; // jump to the location hash id
history.pushState("", "", category); // make the url pretty again
return false; // dont append #category to the pretty url
});
但是,当用户向下滚动并且新类别开始出现时,URL 应更改为示例。com/category2(因为类别 1 是第一个类别,将出现在初始页面加载时),用户继续滚动然后 URL 更改为 example.com/category3。现在,如果用户向上滚动,它应该变回 example.com/category2。我只是在监听滚动事件然后检查元素是否在视口中吗?如何处理页面上半部分显示上一个类别而下半部分显示下一个类别的情况?
我的另一个障碍是如何处理直接链接到页面上的特定类别。如果用户直接链接到示例。com/category5 页面应加载并锚定到类别 5 部分。
我应该提一下,我计划使用 jquery.lazy.js 在主容器 div 中加载类别,以减少初始页面加载时间。因此,我无法使用 jquery 向下滚动到某个元素,因为它会计算 element/page 的大小并向下滚动该数量,但是一旦内容加载到其中,它就不再是适当的类别。尽管如果它主要影响更改 URL.
的能力,我愿意改变这种方法
对不起文字墙!我不是在找人为我编写代码,而是在寻找正确的方向!
简短的回答是,我通常使用 history.replaceState()
来完成此操作,但还有很多事情要做。 MDN 描述了这个方法:
Updates the most recent entry on the history stack to have the
specified data, title, and, if provided, URL. The data is treated as
opaque by the DOM; you may specify any JavaScript object that can be
serialized.
在实践中,我创建了一个状态对象,其中不同的状态是我的页面部分。我经常将它与出色的 Waypoints plugin 结合使用,触发基于航路点的状态更改。这段代码很容易理解,请阅读我的评论,这将引导您完成它。
// 1- Sets up state object that will store each page section 'state'
var stateObj = { state0: "group" };
// 2- Setups up waypoint to indicate a new section
var yourNameWaypoint = new Waypoint({
// 3- Target an ID for waypoints to watch, corresponds to a page section
element: document.getElementById('page-id-target'),
handler: function(direction) {
// 4- Create down direction handler
if (direction === 'down') {
// 5- Assign stateObj a page state that corresponds to your page section
stateObj = { state1: "page-section-1" };
history.replaceState(stateObj, "Section Title", "#new-page-section-slug-here");
}
// 6- Do the same thing in the other direction now, reseting the URL to what it was before the waypoint was hit
if (direction === 'up') {
stateObj = { state0: "original-state" };
history.replaceState(stateObj, "Original Page Section Title", "original-page-slug-here");
}
}
});
要使哈希对应于滚动位置有点困难。我修改了一个非常好的脚本来让它工作 (http://jsfiddle.net/ianclark001/rkocah23/),但我会 post 原来的。
这个想法很简单。基本上,您正在读取在 init
中在上面的函数中创建的每个 URL 哈希,然后在匹配时使用 history.pushState()
将其输入到函数的 scrollToCurrent
部分。我发现我喜欢在滚动动画中放置一点延迟,以使行为感觉更正常(设置为低于 500 毫秒,但您可以调整它)。
(function(document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: 50,
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function() {
this.scrollToCurrent();
$(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
$('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function() {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* @param {String} href
* @return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function(href, pushToHistory) {
var match, anchorOffset;
if(!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if(match) {
anchorOffset = $(match).offset().top - this.getFixedOffset();
$('html, body').delay(500).animate({ scrollTop: anchorOffset});
// Add the state to history as-per normal anchor links
if(HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* Attempt to scroll to the current location's hash.
*/
scrollToCurrent: function(e) {
if(this.scrollIfAnchor(window.location.hash) && e) {
e.preventDefault();
}
},
/**
* If the click event's target was an anchor, fix the scroll position.
*/
delegateAnchors: function(e) {
var elem = e.target;
if(this.scrollIfAnchor(elem.getAttribute('href'), true)) {
e.preventDefault();
}
}
};
$(document).ready($.proxy(anchorScrolls, 'init'));
})(window.document, window.history, window.location);
我的任务是创建一个长页面,其中包含各种小文章供用户滚动浏览。每个文章部分都有一个类别,我需要 URL 来反映正在查看的类别。我知道 history.pushState()
可用于在触发时更改 URL。但是我不清楚的是物流。
我已经找到了一种方法,可以使用以下代码将主菜单链接跳转到适当的类别,而且似乎工作正常。
$('a.js-link').on('click',function (e) {
e.preventDefault; // dont actually follow the link
var category = $(this).data('category-id'); // get the id we should anchor to
location.hash = '#' + category; // jump to the location hash id
history.pushState("", "", category); // make the url pretty again
return false; // dont append #category to the pretty url
});
但是,当用户向下滚动并且新类别开始出现时,URL 应更改为示例。com/category2(因为类别 1 是第一个类别,将出现在初始页面加载时),用户继续滚动然后 URL 更改为 example.com/category3。现在,如果用户向上滚动,它应该变回 example.com/category2。我只是在监听滚动事件然后检查元素是否在视口中吗?如何处理页面上半部分显示上一个类别而下半部分显示下一个类别的情况?
我的另一个障碍是如何处理直接链接到页面上的特定类别。如果用户直接链接到示例。com/category5 页面应加载并锚定到类别 5 部分。
我应该提一下,我计划使用 jquery.lazy.js 在主容器 div 中加载类别,以减少初始页面加载时间。因此,我无法使用 jquery 向下滚动到某个元素,因为它会计算 element/page 的大小并向下滚动该数量,但是一旦内容加载到其中,它就不再是适当的类别。尽管如果它主要影响更改 URL.
的能力,我愿意改变这种方法对不起文字墙!我不是在找人为我编写代码,而是在寻找正确的方向!
简短的回答是,我通常使用 history.replaceState()
来完成此操作,但还有很多事情要做。 MDN 描述了这个方法:
Updates the most recent entry on the history stack to have the specified data, title, and, if provided, URL. The data is treated as opaque by the DOM; you may specify any JavaScript object that can be serialized.
在实践中,我创建了一个状态对象,其中不同的状态是我的页面部分。我经常将它与出色的 Waypoints plugin 结合使用,触发基于航路点的状态更改。这段代码很容易理解,请阅读我的评论,这将引导您完成它。
// 1- Sets up state object that will store each page section 'state'
var stateObj = { state0: "group" };
// 2- Setups up waypoint to indicate a new section
var yourNameWaypoint = new Waypoint({
// 3- Target an ID for waypoints to watch, corresponds to a page section
element: document.getElementById('page-id-target'),
handler: function(direction) {
// 4- Create down direction handler
if (direction === 'down') {
// 5- Assign stateObj a page state that corresponds to your page section
stateObj = { state1: "page-section-1" };
history.replaceState(stateObj, "Section Title", "#new-page-section-slug-here");
}
// 6- Do the same thing in the other direction now, reseting the URL to what it was before the waypoint was hit
if (direction === 'up') {
stateObj = { state0: "original-state" };
history.replaceState(stateObj, "Original Page Section Title", "original-page-slug-here");
}
}
});
要使哈希对应于滚动位置有点困难。我修改了一个非常好的脚本来让它工作 (http://jsfiddle.net/ianclark001/rkocah23/),但我会 post 原来的。
这个想法很简单。基本上,您正在读取在 init
中在上面的函数中创建的每个 URL 哈希,然后在匹配时使用 history.pushState()
将其输入到函数的 scrollToCurrent
部分。我发现我喜欢在滚动动画中放置一点延迟,以使行为感觉更正常(设置为低于 500 毫秒,但您可以调整它)。
(function(document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: 50,
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function() {
this.scrollToCurrent();
$(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
$('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function() {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* @param {String} href
* @return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function(href, pushToHistory) {
var match, anchorOffset;
if(!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if(match) {
anchorOffset = $(match).offset().top - this.getFixedOffset();
$('html, body').delay(500).animate({ scrollTop: anchorOffset});
// Add the state to history as-per normal anchor links
if(HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* Attempt to scroll to the current location's hash.
*/
scrollToCurrent: function(e) {
if(this.scrollIfAnchor(window.location.hash) && e) {
e.preventDefault();
}
},
/**
* If the click event's target was an anchor, fix the scroll position.
*/
delegateAnchors: function(e) {
var elem = e.target;
if(this.scrollIfAnchor(elem.getAttribute('href'), true)) {
e.preventDefault();
}
}
};
$(document).ready($.proxy(anchorScrolls, 'init'));
})(window.document, window.history, window.location);