当超过 div 元素时,使 <a> 在菜单中处于活动状态

Making <a> active in menu when over div-element

我试图在滚动到滚动到的元素上时激活我的链接。我已经尝试了两天找到的每一行代码。但没有任何效果。我正在使用 cferdinandi 的 smoothscroll 脚本。

<div data-scroll-header id="menu">
    <div id="menu-content">
            <a data-scroll href="#weare">Company</a>
            <a data-scroll href="#product">Product</a>
            <a data-scroll href="#technical">Technical specifications</a>
            <a data-scroll href="#footer">Get in touch</a>
    </div>
</div>

有了这个 "smooth-scroll" 脚本。

<script>
    smoothScroll.init({
        updateURL: true,
    });
</script>

更新(CSS):

#menu {
    background-color:white;
    position:fixed;
    top:0;
    left:0;
    height:90px;
    width:100%;
    z-index:99999;
    transition: top 0.2s ease-in-out;
}

#menu {
    will-change: transform;
    transition: transform 200ms linear;
}

.headroom--pinned {
    transform: translateY(0%);
}

.headroom--unpinned {
    transform: translateY(-100%);
}

.active {
    font-family:"Aero Bold", sans-serif;
}

#menu-logo {
    max-height:26px;
    display:block;
    margin:auto;
    position:absolute;
    top:20px;
    left:0;
    right:0;
    pointer-events: none;
}

#menu-content {
    position:absolute;
    width:75%;
    height:12px;
    line-height:12px;
    bottom:0;
    left:12.5%;
    right:12.5%;
    margin-bottom:20px;
    text-align:center;
}

#menu-content a {
    padding:0 10px 0 10px;
    font-weight:thin;
    color:#03372B;
    color:#666666;
    text-decoration:none;
    font-size:12px;
    font-family:"Aero Book", Helvetica, Arial, sans-serif;
}

#menu-content a:hover {
    color:#03372B;
    transition:0.3s;
}

更新它在工作,但它不工作:O:

更新(修复了什么):

#menu-content {
    position:absolute;
    width:75%;
    height:12px;
    line-height:12px;
    bottom:0;
    left:12.5%;
    right:12.5%;
    margin-bottom:20px;
    text-align:center;
}

.nav {
    padding:0 10px 0 10px;
    color:#666666;
    text-decoration:none;
    font-size:12px;
    font-family:"Aero Book", Helvetica, Arial, sans-serif;
}

.active {
    text-decoration:none;
    font-family: "Aero Bold", Helvetica, Arial, sans-serif;
}

给链接一个 "nav" 的 class 并添加一个回调

这是实际代码:

window.onload=function() {
  smoothScroll.init({
    updateURL: true,
    callback: function(anchor, toggle) {
      console.log(anchor);
      var link=document.querySelector("a[href='"+anchor+"']");
      var links = document.querySelectorAll("#menu-content .nav");
      for (var i = 0; i < links.length; i++) {
        links[i].classList.toggle('active', 0);
      }
      link.classList.toggle("active", toggle);
    }
  });
}

示例(必须在 smoothscroll 代码中复制)

(function (root, factory) {
 if ( typeof define === 'function' && define.amd ) {
  define([], factory(root));
 } else if ( typeof exports === 'object' ) {
  module.exports = factory(root);
 } else {
  root.smoothScroll = factory(root);
 }
})(typeof global !== 'undefined' ? global : this.window || this.global, function (root) {

 'use strict';

 //
 // Variables
 //

 var smoothScroll = {}; // Object for public APIs
 var supports = 'querySelector' in document && 'addEventListener' in root; // Feature test
 var settings, eventTimeout, fixedHeader, headerHeight, animationInterval;

 // Default settings
 var defaults = {
  selector: '[data-scroll]',
  selectorHeader: '[data-scroll-header]',
  speed: 500,
  easing: 'easeInOutCubic',
  offset: 0,
  updateURL: true,
  callback: function () {}
 };


 //
 // Methods
 //

 /**
  * Merge two or more objects. Returns a new object.
  * @private
  * @param {Boolean}  deep     If true, do a deep (or recursive) merge [optional]
  * @param {Object}   objects  The objects to merge together
  * @returns {Object}          Merged values of defaults and options
  */
 var extend = function () {

  // Variables
  var extended = {};
  var deep = false;
  var i = 0;
  var length = arguments.length;

  // Check if a deep merge
  if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
   deep = arguments[0];
   i++;
  }

  // Merge the object into the extended object
  var merge = function (obj) {
   for ( var prop in obj ) {
    if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {
     // If deep merge and property is an object, merge properties
     if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) {
      extended[prop] = extend( true, extended[prop], obj[prop] );
     } else {
      extended[prop] = obj[prop];
     }
    }
   }
  };

  // Loop through each object and conduct a merge
  for ( ; i < length; i++ ) {
   var obj = arguments[i];
   merge(obj);
  }

  return extended;

 };

 /**
  * Get the height of an element.
  * @private
  * @param  {Node} elem The element to get the height of
  * @return {Number}    The element's height in pixels
  */
 var getHeight = function ( elem ) {
  return Math.max( elem.scrollHeight, elem.offsetHeight, elem.clientHeight );
 };

 /**
  * Get the closest matching element up the DOM tree.
  * @private
  * @param  {Element} elem     Starting element
  * @param  {String}  selector Selector to match against (class, ID, data attribute, or tag)
  * @return {Boolean|Element}  Returns null if not match found
  */
 var getClosest = function ( elem, selector ) {

  // Variables
  var firstChar = selector.charAt(0);
  var supports = 'classList' in document.documentElement;
  var attribute, value;

  // If selector is a data attribute, split attribute from value
  if ( firstChar === '[' ) {
   selector = selector.substr(1, selector.length - 2);
   attribute = selector.split( '=' );

   if ( attribute.length > 1 ) {
    value = true;
    attribute[1] = attribute[1].replace( /"/g, '' ).replace( /'/g, '' );
   }
  }

  // Get closest match
  for ( ; elem && elem !== document && elem.nodeType === 1; elem = elem.parentNode ) {

   // If selector is a class
   if ( firstChar === '.' ) {
    if ( supports ) {
     if ( elem.classList.contains( selector.substr(1) ) ) {
      return elem;
     }
    } else {
     if ( new RegExp('(^|\s)' + selector.substr(1) + '(\s|$)').test( elem.className ) ) {
      return elem;
     }
    }
   }

   // If selector is an ID
   if ( firstChar === '#' ) {
    if ( elem.id === selector.substr(1) ) {
     return elem;
    }
   }

   // If selector is a data attribute
   if ( firstChar === '[' ) {
    if ( elem.hasAttribute( attribute[0] ) ) {
     if ( value ) {
      if ( elem.getAttribute( attribute[0] ) === attribute[1] ) {
       return elem;
      }
     } else {
      return elem;
     }
    }
   }

   // If selector is a tag
   if ( elem.tagName.toLowerCase() === selector ) {
    return elem;
   }

  }

  return null;

 };

 /**
  * Escape special characters for use with querySelector
  * @public
  * @param {String} id The anchor ID to escape
  * @author Mathias Bynens
  * @link https://github.com/mathiasbynens/CSS.escape
  */
 smoothScroll.escapeCharacters = function ( id ) {

  // Remove leading hash
  if ( id.charAt(0) === '#' ) {
   id = id.substr(1);
  }

  var string = String(id);
  var length = string.length;
  var index = -1;
  var codeUnit;
  var result = '';
  var firstCodeUnit = string.charCodeAt(0);
  while (++index < length) {
   codeUnit = string.charCodeAt(index);
   // Note: there’s no need to special-case astral symbols, surrogate
   // pairs, or lone surrogates.

   // If the character is NULL (U+0000), then throw an
   // `InvalidCharacterError` exception and terminate these steps.
   if (codeUnit === 0x0000) {
    throw new InvalidCharacterError(
     'Invalid character: the input contains U+0000.'
    );
   }

   if (
    // If the character is in the range [-F] (U+0001 to U+001F) or is
    // U+007F, […]
    (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
    // If the character is the first character and is in the range [0-9]
    // (U+0030 to U+0039), […]
    (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
    // If the character is the second character and is in the range [0-9]
    // (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
    (
     index === 1 &&
     codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
     firstCodeUnit === 0x002D
    )
   ) {
    // http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point
    result += '\' + codeUnit.toString(16) + ' ';
    continue;
   }

   // If the character is not handled by one of the above rules and is
   // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
   // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
   // U+005A), or [a-z] (U+0061 to U+007A), […]
   if (
    codeUnit >= 0x0080 ||
    codeUnit === 0x002D ||
    codeUnit === 0x005F ||
    codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
    codeUnit >= 0x0041 && codeUnit <= 0x005A ||
    codeUnit >= 0x0061 && codeUnit <= 0x007A
   ) {
    // the character itself
    result += string.charAt(index);
    continue;
   }

   // Otherwise, the escaped character.
   // http://dev.w3.org/csswg/cssom/#escape-a-character
   result += '\' + string.charAt(index);

  }

  return '#' + result;

 };

 /**
  * Calculate the easing pattern
  * @private
  * @link https://gist.github.com/gre/1650294
  * @param {String} type Easing pattern
  * @param {Number} time Time animation should take to complete
  * @returns {Number}
  */
 var easingPattern = function ( type, time ) {
  var pattern;
  if ( type === 'easeInQuad' ) pattern = time * time; // accelerating from zero velocity
  if ( type === 'easeOutQuad' ) pattern = time * (2 - time); // decelerating to zero velocity
  if ( type === 'easeInOutQuad' ) pattern = time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration
  if ( type === 'easeInCubic' ) pattern = time * time * time; // accelerating from zero velocity
  if ( type === 'easeOutCubic' ) pattern = (--time) * time * time + 1; // decelerating to zero velocity
  if ( type === 'easeInOutCubic' ) pattern = time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration
  if ( type === 'easeInQuart' ) pattern = time * time * time * time; // accelerating from zero velocity
  if ( type === 'easeOutQuart' ) pattern = 1 - (--time) * time * time * time; // decelerating to zero velocity
  if ( type === 'easeInOutQuart' ) pattern = time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration
  if ( type === 'easeInQuint' ) pattern = time * time * time * time * time; // accelerating from zero velocity
  if ( type === 'easeOutQuint' ) pattern = 1 + (--time) * time * time * time * time; // decelerating to zero velocity
  if ( type === 'easeInOutQuint' ) pattern = time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration
  return pattern || time; // no easing, no acceleration
 };

 /**
  * Calculate how far to scroll
  * @private
  * @param {Element} anchor The anchor element to scroll to
  * @param {Number} headerHeight Height of a fixed header, if any
  * @param {Number} offset Number of pixels by which to offset scroll
  * @returns {Number}
  */
 var getEndLocation = function ( anchor, headerHeight, offset ) {
  var location = 0;
  if (anchor.offsetParent) {
   do {
    location += anchor.offsetTop;
    anchor = anchor.offsetParent;
   } while (anchor);
  }
  location = Math.max(location - headerHeight - offset, 0);
  return Math.min(location, getDocumentHeight() - getViewportHeight());
 };

 /**
  * Determine the viewport's height
  * @private
  * @returns {Number}
  */
 var getViewportHeight = function() {
         return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
     };

 /**
  * Determine the document's height
  * @private
  * @returns {Number}
  */
 var getDocumentHeight = function () {
  return Math.max(
   root.document.body.scrollHeight, root.document.documentElement.scrollHeight,
   root.document.body.offsetHeight, root.document.documentElement.offsetHeight,
   root.document.body.clientHeight, root.document.documentElement.clientHeight
  );
 };

 /**
  * Convert data-options attribute into an object of key/value pairs
  * @private
  * @param {String} options Link-specific options as a data attribute string
  * @returns {Object}
  */
 var getDataOptions = function ( options ) {
  return !options || !(typeof JSON === 'object' && typeof JSON.parse === 'function') ? {} : JSON.parse( options );
 };

 /**
  * Update the URL
  * @private
  * @param {Element} anchor The element to scroll to
  * @param {Boolean} url Whether or not to update the URL history
  */
 var updateUrl = function ( anchor, url ) {
  if ( root.history.pushState && (url || url === 'true') && root.location.protocol !== 'file:' ) {
   root.history.pushState( null, null, [root.location.protocol, '//', root.location.host, root.location.pathname, root.location.search, anchor].join('') );
  }
 };

 var getHeaderHeight = function ( header ) {
  return header === null ? 0 : ( getHeight( header ) + header.offsetTop );
 };

 /**
  * Start/stop the scrolling animation
  * @public
  * @param {Element} anchor The element to scroll to
  * @param {Element} toggle The element that toggled the scroll event
  * @param {Object} options
  */
 smoothScroll.animateScroll = function ( anchor, toggle, options ) {

  // Options and overrides
  var overrides = getDataOptions( toggle ? toggle.getAttribute('data-options') : null );
  var animateSettings = extend( settings || defaults, options || {}, overrides ); // Merge user options with defaults

  // Selectors and variables
  var isNum = Object.prototype.toString.call( anchor ) === '[object Number]' ? true : false;
  var hash = smoothScroll.escapeCharacters( anchor );
  var anchorElem = isNum ? null : ( hash === '#' ? root.document.documentElement : root.document.querySelector( hash ) );
  if ( !isNum && !anchorElem ) return;
  var startLocation = root.pageYOffset; // Current location on the page
  if ( !fixedHeader ) { fixedHeader = root.document.querySelector( animateSettings.selectorHeader ); }  // Get the fixed header if not already set
  if ( !headerHeight ) { headerHeight = getHeaderHeight( fixedHeader ); } // Get the height of a fixed header if one exists and not already set
  var endLocation = isNum ? anchor : getEndLocation( anchorElem, headerHeight, parseInt(animateSettings.offset, 10) ); // Location to scroll to
  var distance = endLocation - startLocation; // distance to travel
  var documentHeight = getDocumentHeight();
  var timeLapsed = 0;
  var percentage, position;

  // Update URL
  if ( !isNum ) {
   updateUrl( anchor, animateSettings.updateURL );
  }

  /**
   * Stop the scroll animation when it reaches its target (or the bottom/top of page)
   * @private
   * @param {Number} position Current position on the page
   * @param {Number} endLocation Scroll to location
   * @param {Number} animationInterval How much to scroll on this loop
   */
  var stopAnimateScroll = function ( position, endLocation, animationInterval ) {
   var currentLocation = root.pageYOffset;
   if ( position == endLocation || currentLocation == endLocation || ( (root.innerHeight + currentLocation) >=

    documentHeight ) ) {
    clearInterval(animationInterval);

    // If scroll target is an anchor, bring it into focus
    if ( !isNum ) {
     anchorElem.focus();
     if ( document.activeElement.id !== anchorElem.id ) {
      anchorElem.setAttribute( 'tabindex', '-1' );
      anchorElem.focus();
      anchorElem.style.outline = 'none';
     }
    }
    animateSettings.callback( anchor, toggle ); // Run callbacks after animation complete
   }
  };

  /**
   * Loop scrolling animation
   * @private
   */
  var loopAnimateScroll = function () {
   timeLapsed += 16;
   percentage = ( timeLapsed / parseInt(animateSettings.speed, 10) );
   percentage = ( percentage > 1 ) ? 1 : percentage;
   position = startLocation + ( distance * easingPattern(animateSettings.easing, percentage) );
   root.scrollTo( 0, Math.floor(position) );
   stopAnimateScroll(position, endLocation, animationInterval);
  };

  /**
   * Set interval timer
   * @private
   */
  var startAnimateScroll = function () {
   clearInterval(animationInterval);
   animationInterval = setInterval(loopAnimateScroll, 16);
  };

  /**
   * Reset position to fix weird iOS bug
   * @link https://github.com/cferdinandi/smooth-scroll/issues/45
   */
  if ( root.pageYOffset === 0 ) {
   root.scrollTo( 0, 0 );
  }

  // Start scrolling animation
  startAnimateScroll();

 };

 /**
  * If smooth scroll element clicked, animate scroll
  * @private
  */
 var eventHandler = function (event) {

  // Don't run if right-click or command/control + click
  if ( event.button !== 0 || event.metaKey || event.ctrlKey ) return;

  // If a smooth scroll link, animate it
  var toggle = getClosest( event.target, settings.selector );
  if ( toggle && toggle.tagName.toLowerCase() === 'a' ) {

   // Check that link is an anchor and points to current page
   if ( toggle.hostname !== root.location.hostname || toggle.pathname !== root.location.pathname || !/#/.test(toggle.href) ) return;

   event.preventDefault(); // Prevent default click event
   smoothScroll.animateScroll( toggle.hash, toggle, settings); // Animate scroll

  }

 };

 /**
  * On window scroll and resize, only run events at a rate of 15fps for better performance
  * @private
  * @param  {Function} eventTimeout Timeout function
  * @param  {Object} settings
  */
 var eventThrottler = function (event) {
  if ( !eventTimeout ) {
   eventTimeout = setTimeout(function() {
    eventTimeout = null; // Reset timeout
    headerHeight = getHeaderHeight( fixedHeader ); // Get the height of a fixed header if one exists
   }, 66);
  }
 };

 /**
  * Destroy the current initialization.
  * @public
  */
 smoothScroll.destroy = function () {

  // If plugin isn't already initialized, stop
  if ( !settings ) return;

  // Remove event listeners
  root.document.removeEventListener( 'click', eventHandler, false );
  root.removeEventListener( 'resize', eventThrottler, false );

  // Reset varaibles
  settings = null;
  eventTimeout = null;
  fixedHeader = null;
  headerHeight = null;
  animationInterval = null;
 };

 /**
  * Initialize Smooth Scroll
  * @public
  * @param {Object} options User settings
  */
 smoothScroll.init = function ( options ) {

  // feature test
  if ( !supports ) return;

  // Destroy any existing initializations
  smoothScroll.destroy();

  // Selectors and variables
  settings = extend( defaults, options || {} ); // Merge user options with defaults
  fixedHeader = root.document.querySelector( settings.selectorHeader ); // Get the fixed header
  headerHeight = getHeaderHeight( fixedHeader );

  // When a toggle is clicked, run the click handler
  root.document.addEventListener('click', eventHandler, false );
  if ( fixedHeader ) { root.addEventListener( 'resize', eventThrottler, false ); }

 };


 //
 // Public APIs
 //

 return smoothScroll;

});
window.onload=function() {
  smoothScroll.init({
    updateURL: true,
    callback: function(anchor, toggle) {
      console.log(anchor);
      var link=document.querySelector("a[href='"+anchor+"']");
      var links = document.querySelectorAll("#menu-content .nav");
      for (var i = 0; i < links.length; i++) {
        links[i].classList.toggle('active', 0);
      }
      link.classList.toggle("active", toggle);
    }
  });
}
a { text-decoration:none; }
.active { text-decoration:underline; font-family:"Aero Bold", sans-serif;
}
<div data-scroll-header id="menu">
  <div id="menu-content">
    <a class="nav" data-scroll href="#weare">Company</a>
    <a class="nav" data-scroll href="#product">Product</a>
    <a class="nav" data-scroll href="#technical">Technical specifications</a>
    <a class="nav" data-scroll href="#footer">Get in touch</a>
  </div>
</div>
<hr />
<div id="weare">We are what</div>
<div id="product">product</div>