如何使用 Bootstrap ScrollSpy 在身体上使用多个目标

问题描述 投票:0回答:3

我想将 Bootstrap ScrollSpy 与多个导航、我的主导航和垂直子导航一起使用。

我尝试了多种方法,例如:

  • <body data-spy="scroll" data-target="#main-nav #subnav">
  • <body data-spy="scroll" data-target="#main-nav, #subnav">
  • $('body').scrollspy({target: "#main-nav"});

    $('body').scrollspy({target: "#subnav"});
  • $('body').scrollspy({target: "#main-nav"}, {target: "#subnav"});
  • 放置包装div:
    <body data-spy="scroll" data-target="#main-nav"><div data-spy="scroll" data-target="#subnav">

但没有任何效果......

我怎样才能实现这个目标?

谢谢!

jquery twitter-bootstrap multiple-instances scrollspy
3个回答
12
投票

好吧,我找到了一种方法来实现我想要的。

如果我没有定义scrollSpy的目标,我的所有导航链接都会被考虑:

$('body').scrollspy({target: ''});

1
投票

我知道,这个问题很老了,但这里是我对多目标滚动间谍的实现。 就像这样使用它:

<body data-multiple-spy="scroll" data-target="#bs-example-navbar-collapse-1,#features-navbar .navbar-collapse" data-offset="70"</body>

插件:

+function ($) {
'use strict';

// Multiple SCROLLSPY CLASS DEFINITION
// ===================================

function MultipleScrollSpy(element, options) {
    this.$body = $(document.body)
    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
    this.options = $.extend({}, MultipleScrollSpy.DEFAULTS, options)
    var target = (this.options.target || '').split(',');
    this.selector = target.map(function (s) { return s + ' .nav li > a' })
    this.offsets = []
    this.targets = []
    this.activeTarget = []
    this.scrollHeight = 0

    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
    this.refresh()
    this.process()
}

MultipleScrollSpy.VERSION = '0.0.1'

MultipleScrollSpy.DEFAULTS = {
    offset: 10
}

MultipleScrollSpy.prototype.getScrollHeight = function () {
    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
}

MultipleScrollSpy.prototype.refresh = function () {
    var that = this
    var offsetMethod = 'offset'
    var offsetBase = 0

    this.offsets = []
    this.targets = []
    this.activeTarget = []
    this.scrollHeight = this.getScrollHeight()

    if (!$.isWindow(this.$scrollElement[0])) {
        offsetMethod = 'position'
        offsetBase = this.$scrollElement.scrollTop()
    }

    for (var i = 0; i < this.selector.length; i++) {
        that.offsets[i] = [];
        that.targets[i] = [];
        this.$body
            .find(this.selector[i])
            .map(function () {
                var $el = $(this)
                var href = $el.data('target') || $el.attr('href')
                var $href = /^#./.test(href) && $(href)

                return ($href &&
                    $href.length &&
                    $href.is(':visible') &&
                    [[$href[offsetMethod]().top + offsetBase, href]]) ||
                    null
            })
            .sort(function (a, b) { return a[0] - b[0] })
            .each(function () {
                that.offsets[i].push(this[0])
                that.targets[i].push(this[1])
            })
    }
}

MultipleScrollSpy.prototype.process = function () {
    for (var k = 0; k < this.selector.length; k++) {
        var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
        var scrollHeight = this.getScrollHeight()
        var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
        var offsets = this.offsets[k] || []
        var targets = this.targets[k] || []
        var activeTarget = this.activeTarget[k]
        var i

        if (this.scrollHeight != scrollHeight) {
            this.refresh()
        }

        if (scrollTop >= maxScroll) {
            return activeTarget[k] != (i = targets[targets.length - 1]) && this.activate(k, i)
        }

        if (activeTarget && scrollTop < offsets[0]) {
            this.activeTarget[k] = null
            return this.clear(k)
        }

        for (i = offsets.length; i--;) {
            activeTarget != targets[i] &&
                scrollTop >= offsets[i] &&
                (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) &&
                this.activate(k, targets[i])
        }
    }
}

MultipleScrollSpy.prototype.activate = function (index, target) {
    this.activeTarget[index] = target

    this.clear(index)

    var selector = this.selector[index] +
        '[data-target="' + target + '"],' +
        this.selector[index] + '[href="' + target + '"]'

    var active = $(selector)
        .parents('li')
        .addClass('active')

    if (active.parent('.dropdown-menu').length) {
        active = active
            .closest('li.dropdown')
            .addClass('active')
    }

    active.trigger('activate.bs.scrollspy')
}

MultipleScrollSpy.prototype.clear = function (index) {
    $(this.selector[index])
        .parentsUntil(this.options.target, '.active')
        .removeClass('active')
}


// MULTIPLE SCROLLSPY PLUGIN DEFINITION
// ====================================

function Plugin(option) {
    return this.each(function () {
        var $this = $(this)
        var data = $this.data('bs.scrollspy')
        var options = typeof option == 'object' && option

        if (!data) $this.data('bs.scrollspy', (data = new MultipleScrollSpy(this, options)))
        if (typeof option == 'string') data[option]()
    })
}

var old = $.fn.multipleScrollspy;

$.fn.multipleScrollspy = Plugin;
$.fn.multipleScrollspy.Constructor = MultipleScrollSpy;


    // MULTIPLE SCROLLSPY NO CONFLICT
    // =============================

    ($.fn.multipleScrollspy || {}).noConflict = function () {
        $.fn.multipleScrollspy = old
        return this
    }


// MULTIPLE SCROLLSPY DATA-API
// ===========================

$(window).on('load.bs.scrollspy.data-api', function () {
    $('[data-multiple-spy="scroll"]').each(function () {
        var $spy = $(this)
        Plugin.call($spy, $spy.data())
    })
})

}(jQuery);

0
投票

还有一个非常古老的问题的答案。

我的网站左侧显示一个垂直导航栏,对于宽屏,

#navbar
很宽,包含图标和文本,它使用
ScrollSpy
来突出显示您当前滚动到的部分。对于较小的屏幕,
#navbar-small
仅是图标,没有文本,因此它更窄,它是使用引导断点来实现的,当屏幕较宽时隐藏小栏,反之亦然。

使用MutationObserver(当这个问题最初发布时,它要么不可用,要么非常闪亮),你可以设置一个回调,以便在目标节点和可选的子节点发生变化时运行,所以我的解决方法是针对广泛的

#navbar
是我网站上
ScrollSpy
的目标,然后根据需要使用其更改将
active
类添加/删除到我的
#navbar-small
元素子元素(我实际上只是复制
#navbar 中的所有类属性) 
#navbar-small
因为对我来说它们是相同的)。

所以我的解决方案:

// Make this function do whatever logic you need it to do, I'm lucky that I just
// want to copy the exact same classes from #navbar's children to
// #navbar-small's children.
//
const duplicateScrollSpyNavActive = function () {
  let navbarSmallLinks = document
    .getElementById("navbar-small")
    .getElementsByClassName("nav-link");

  for (let link of navbarSmallLinks) {
    link.setAttribute(
      "class",
      document
        .getElementById(link.getAttribute("id").replace("small-", ""))
        .getAttribute("class"),
    );
  }
};

// This is the mostly generic part, just make duplicateScrollSpyNavActive above
// do whatever you want for your use-case.
window.addEventListener("load", function (event) {
  //
  // Make target be the element id that is the same as `data-bs-target` in your
  // HTML
  //
  const targetNode = document.getElementById("navbar");

  // We only really care about the active class being added or removed so we
  // can just filter for "class" but we need subtree to be true so we can
  // detect changes on the children (the actual links that get highlighted).
  //
  const options = { subtree: true, attributeFilter: ["class"] };
  const observer = new MutationObserver(duplicateScrollSpyNavActive);

  observer.observe(targetNode, options);
});

© www.soinside.com 2019 - 2024. All rights reserved.