javascript - 如何更改长滚动页面上的 URL?

标签 javascript jquery html scroll fragment-identifier

我的任务是创建一个长页面,其中包含各种小文章供用户滚动浏览。每个文章部分都有一个类别,我需要 URL 来反射(reflect)正在查看的类别。我知道 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 应更改为 example.com/category2(因为类别 1 是第一个,并且将出现在初始页面加载时),用户继续滚动然后 URL 更改为example.com/category3。现在,如果用户向上滚动,它应该变回 example.com/category2。我只是在监听滚动事件然后检查元素是否在视口(viewport)中吗?如何处理页面上半部分显示上一个类别而下半部分显示下一个类别的情况?

我的另一个障碍是如何处理直接链接到页面上的特定类别。如果用户直接链接到 example.com/category5,则应加载该页面并将其锚定到 category5 部分。

我应该提到我计划使用 jquery.lazy.js加载类别的主容器 div,以减少初始页面加载时间。因此,我不能使用 jquery 向下滚动到一个元素,因为它会计算元素/页面的大小并向下滚动该数量,但是一旦内容加载到其中,它就不再是 View 中的适当类别。如果它主要影响更改 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/),但我会在这里发布原始脚本。

这个想法很简单。基本上,您正在读取在 init 中在上面的函数中创建的每个 URL 哈希,然后在它们匹配时将其输入函数的 scrollToCurrent 部分,使用 history。推状态()。我发现我喜欢在滚动动画中放置一点延迟以使行为感觉更正常(设置为低于 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);

关于javascript - 如何更改长滚动页面上的 URL?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39039859/

相关文章:

javascript - 获取 touchstart 事件中的元素位置

javascript - 将鼠标悬停在菜单元素上以显示子菜单

php - 如何在jquery中编写php代码

javascript - 突出显示 html 元素

html - 如何在没有媒体CSS的情况下制作图像比例

html - 如何获得列表项显示 block ?

javascript - Swagger 客户一代

javascript - php中的子串js

javascript - 在 Electron 中没有 "switching"应用程序的情况下,如何将窗口带到前面?

jquery - jCarousel 无限循环 - 未设置宽度或高度