Comparing version 3.5.0 to 4.0.1
390
gulpfile.js
/** | ||
* Gulp Packages | ||
* Settings | ||
* Turn on/off build features | ||
*/ | ||
// General | ||
var gulp = require('gulp'); | ||
var fs = require('fs'); | ||
var del = require('del'); | ||
var lazypipe = require('lazypipe'); | ||
var plumber = require('gulp-plumber'); | ||
var flatten = require('gulp-flatten'); | ||
var tap = require('gulp-tap'); | ||
var rename = require('gulp-rename'); | ||
var header = require('gulp-header'); | ||
var footer = require('gulp-footer'); | ||
var watch = require('gulp-watch'); | ||
var livereload = require('gulp-livereload'); | ||
var package = require('./package.json'); | ||
var settings = { | ||
clean: true, | ||
scripts: true, | ||
polyfills: true, | ||
styles: true, | ||
svgs: true, | ||
copy: true, | ||
reload: true | ||
}; | ||
// Scripts and tests | ||
var jshint = require('gulp-jshint'); | ||
var stylish = require('jshint-stylish'); | ||
var concat = require('gulp-concat'); | ||
var uglify = require('gulp-uglify'); | ||
var optimizejs = require('gulp-optimize-js'); | ||
// Docs | ||
var markdown = require('gulp-markdown'); | ||
var fileinclude = require('gulp-file-include'); | ||
/** | ||
@@ -37,14 +22,22 @@ * Paths to project folders | ||
var paths = { | ||
input: 'src/**/*', | ||
input: 'src/', | ||
output: 'dist/', | ||
scripts: { | ||
input: 'src/js/*', | ||
output: 'dist/js/' | ||
polyfills: '.polyfill.js', | ||
output: 'dist/' | ||
}, | ||
docs: { | ||
input: 'src/docs/*.{html,md,markdown}', | ||
output: 'docs/', | ||
templates: 'src/docs/_templates/', | ||
assets: 'src/docs/assets/**' | ||
} | ||
styles: { | ||
input: 'src/sass/**/*.{scss,sass}', | ||
output: 'dist/css/' | ||
}, | ||
svgs: { | ||
input: 'src/svg/*.svg', | ||
output: 'dist/svg/' | ||
}, | ||
copy: { | ||
input: 'src/copy/**/*', | ||
output: './' | ||
}, | ||
reload: './' | ||
}; | ||
@@ -58,14 +51,15 @@ | ||
var banner = { | ||
full : | ||
full: | ||
'/*!\n' + | ||
' * <%= package.name %> v<%= package.version %>: <%= package.description %>\n' + | ||
' * <%= package.name %> v<%= package.version %>\n' + | ||
' * <%= package.description %>\n' + | ||
' * (c) ' + new Date().getFullYear() + ' <%= package.author.name %>\n' + | ||
' * MIT License\n' + | ||
' * <%= package.license %> License\n' + | ||
' * <%= package.repository.url %>\n' + | ||
' */\n\n', | ||
min : | ||
min: | ||
'/*!' + | ||
' <%= package.name %> v<%= package.version %>' + | ||
' | (c) ' + new Date().getFullYear() + ' <%= package.author.name %>' + | ||
' | MIT License' + | ||
' | <%= package.license %> License' + | ||
' | <%= package.repository.url %>' + | ||
@@ -77,126 +71,248 @@ ' */\n' | ||
/** | ||
* Gulp Taks | ||
* Gulp Packages | ||
*/ | ||
// General | ||
var {gulp, src, dest, watch, series, parallel} = require('gulp'); | ||
var del = require('del'); | ||
var flatmap = require('gulp-flatmap'); | ||
var lazypipe = require('lazypipe'); | ||
var rename = require('gulp-rename'); | ||
var header = require('gulp-header'); | ||
var package = require('./package.json'); | ||
// Scripts | ||
var jshint = require('gulp-jshint'); | ||
var stylish = require('jshint-stylish'); | ||
var concat = require('gulp-concat'); | ||
var uglify = require('gulp-terser'); | ||
var optimizejs = require('gulp-optimize-js'); | ||
// Styles | ||
var sass = require('gulp-sass'); | ||
var prefix = require('gulp-autoprefixer'); | ||
var minify = require('gulp-cssnano'); | ||
// SVGs | ||
var svgmin = require('gulp-svgmin'); | ||
// BrowserSync | ||
var browserSync = require('browser-sync'); | ||
/** | ||
* Gulp Tasks | ||
*/ | ||
// Remove pre-existing content from output folders | ||
var cleanDist = function (done) { | ||
// Make sure this feature is activated before running | ||
if (!settings.clean) return done(); | ||
// Clean the dist folder | ||
del.sync([ | ||
paths.output | ||
]); | ||
// Signal completion | ||
return done(); | ||
}; | ||
// Repeated JavaScript tasks | ||
var jsTasks = lazypipe() | ||
.pipe(header, banner.full, {package: package}) | ||
.pipe(optimizejs) | ||
.pipe(dest, paths.scripts.output) | ||
.pipe(rename, {suffix: '.min'}) | ||
.pipe(uglify) | ||
.pipe(optimizejs) | ||
.pipe(header, banner.min, {package: package}) | ||
.pipe(dest, paths.scripts.output); | ||
// Lint, minify, and concatenate scripts | ||
gulp.task('build:scripts', ['clean:dist'], function() { | ||
var jsTasks = lazypipe() | ||
.pipe(header, banner.full, { package : package }) | ||
.pipe(optimizejs) | ||
.pipe(gulp.dest, paths.scripts.output) | ||
.pipe(rename, { suffix: '.min' }) | ||
.pipe(uglify) | ||
.pipe(optimizejs) | ||
.pipe(header, banner.min, { package : package }) | ||
.pipe(gulp.dest, paths.scripts.output); | ||
var buildScripts = function (done) { | ||
return gulp.src(paths.scripts.input) | ||
.pipe(plumber()) | ||
.pipe(tap(function (file, t) { | ||
if ( file.isDirectory() ) { | ||
var name = file.relative + '.js'; | ||
return gulp.src(file.path + '/*.js') | ||
.pipe(concat(name)) | ||
// Make sure this feature is activated before running | ||
if (!settings.scripts) return done(); | ||
// Run tasks on script files | ||
src(paths.scripts.input) | ||
.pipe(flatmap(function(stream, file) { | ||
// If the file is a directory | ||
if (file.isDirectory()) { | ||
// Setup a suffix variable | ||
var suffix = ''; | ||
// If separate polyfill files enabled | ||
if (settings.polyfills) { | ||
// Update the suffix | ||
suffix = '.polyfills'; | ||
// Grab files that aren't polyfills, concatenate them, and process them | ||
src([file.path + '/*.js', '!' + file.path + '/*' + paths.scripts.polyfills]) | ||
.pipe(concat(file.relative + '.js')) | ||
.pipe(jsTasks()); | ||
} | ||
// Grab all files and concatenate them | ||
// If separate polyfills enabled, this will have .polyfills in the filename | ||
src(file.path + '/*.js') | ||
.pipe(concat(file.relative + suffix + '.js')) | ||
.pipe(jsTasks()); | ||
return stream; | ||
} | ||
})) | ||
.pipe(jsTasks()); | ||
}); | ||
// Otherwise, process the file | ||
return stream.pipe(jsTasks()); | ||
})); | ||
// Signal completion | ||
done(); | ||
}; | ||
// Lint scripts | ||
gulp.task('lint:scripts', function () { | ||
return gulp.src(paths.scripts.input) | ||
.pipe(plumber()) | ||
var lintScripts = function (done) { | ||
// Make sure this feature is activated before running | ||
if (!settings.scripts) return done(); | ||
// Lint scripts | ||
src(paths.scripts.input) | ||
.pipe(jshint()) | ||
.pipe(jshint.reporter('jshint-stylish')); | ||
}); | ||
// Remove pre-existing content from output folder | ||
gulp.task('clean:dist', function () { | ||
del.sync([ | ||
paths.output | ||
]); | ||
}); | ||
// Signal completion | ||
done(); | ||
// Generate documentation | ||
gulp.task('build:docs', ['compile', 'clean:docs'], function() { | ||
return gulp.src(paths.docs.input) | ||
.pipe(plumber()) | ||
.pipe(fileinclude({ | ||
prefix: '@@', | ||
basepath: '@file' | ||
}; | ||
// Process, lint, and minify Sass files | ||
var buildStyles = function (done) { | ||
// Make sure this feature is activated before running | ||
if (!settings.styles) return done(); | ||
// Run tasks on all Sass files | ||
src(paths.styles.input) | ||
.pipe(sass({ | ||
outputStyle: 'expanded', | ||
sourceComments: true | ||
})) | ||
.pipe(tap(function (file, t) { | ||
if ( /\.md|\.markdown/.test(file.path) ) { | ||
return t.through(markdown); | ||
.pipe(prefix({ | ||
browsers: ['last 2 version', '> 0.25%'], | ||
cascade: true, | ||
remove: true | ||
})) | ||
.pipe(header(banner.full, { package : package })) | ||
.pipe(dest(paths.styles.output)) | ||
.pipe(rename({suffix: '.min'})) | ||
.pipe(minify({ | ||
discardComments: { | ||
removeAll: true | ||
} | ||
})) | ||
.pipe(header(fs.readFileSync(paths.docs.templates + '/_header.html', 'utf8'))) | ||
.pipe(footer(fs.readFileSync(paths.docs.templates + '/_footer.html', 'utf8'))) | ||
.pipe(gulp.dest(paths.docs.output)); | ||
}); | ||
.pipe(header(banner.min, { package : package })) | ||
.pipe(dest(paths.styles.output)); | ||
// Copy distribution files to docs | ||
gulp.task('copy:dist', ['compile', 'clean:docs'], function() { | ||
return gulp.src(paths.output + '/**') | ||
.pipe(plumber()) | ||
.pipe(gulp.dest(paths.docs.output + '/dist')); | ||
}); | ||
// Signal completion | ||
done(); | ||
// Copy documentation assets to docs | ||
gulp.task('copy:assets', ['clean:docs'], function() { | ||
return gulp.src(paths.docs.assets) | ||
.pipe(plumber()) | ||
.pipe(gulp.dest(paths.docs.output + '/assets')); | ||
}); | ||
}; | ||
// Remove prexisting content from docs folder | ||
gulp.task('clean:docs', function () { | ||
return del.sync(paths.docs.output); | ||
}); | ||
// Optimize SVG files | ||
var buildSVGs = function (done) { | ||
// Spin up livereload server and listen for file changes | ||
gulp.task('listen', function () { | ||
livereload.listen(); | ||
gulp.watch(paths.input).on('change', function(file) { | ||
gulp.start('default'); | ||
gulp.start('refresh'); | ||
// Make sure this feature is activated before running | ||
if (!settings.svgs) return done(); | ||
// Optimize SVG files | ||
src(paths.svgs.input) | ||
.pipe(svgmin()) | ||
.pipe(dest(paths.svgs.output)); | ||
// Signal completion | ||
done(); | ||
}; | ||
// Copy static files into output folder | ||
var copyFiles = function (done) { | ||
// Make sure this feature is activated before running | ||
if (!settings.copy) return done(); | ||
// Copy static files | ||
src(paths.copy.input) | ||
.pipe(dest(paths.copy.output)); | ||
// Signal completion | ||
done(); | ||
}; | ||
// Watch for changes to the src directory | ||
var startServer = function (done) { | ||
// Make sure this feature is activated before running | ||
if (!settings.reload) return done(); | ||
// Initialize BrowserSync | ||
browserSync.init({ | ||
server: { | ||
baseDir: paths.reload | ||
} | ||
}); | ||
}); | ||
// Run livereload after file change | ||
gulp.task('refresh', ['compile', 'docs'], function () { | ||
livereload.changed(); | ||
}); | ||
// Signal completion | ||
done(); | ||
}; | ||
// Reload the browser when files change | ||
var reloadBrowser = function (done) { | ||
if (!settings.reload) return done(); | ||
browserSync.reload(); | ||
done(); | ||
}; | ||
// Watch for changes | ||
var watchSource = function (done) { | ||
watch(paths.input, series(exports.default, reloadBrowser)); | ||
done(); | ||
}; | ||
/** | ||
* Task Runners | ||
* Export Tasks | ||
*/ | ||
// Compile files | ||
gulp.task('compile', [ | ||
'lint:scripts', | ||
'clean:dist', | ||
'build:scripts' | ||
]); | ||
// Default task | ||
// gulp | ||
exports.default = series( | ||
cleanDist, | ||
parallel( | ||
buildScripts, | ||
lintScripts, | ||
buildStyles, | ||
buildSVGs, | ||
copyFiles | ||
) | ||
); | ||
// Generate documentation | ||
gulp.task('docs', [ | ||
'clean:docs', | ||
'build:docs', | ||
'copy:dist', | ||
'copy:assets' | ||
]); | ||
// Compile files and generate docs (default) | ||
gulp.task('default', [ | ||
'compile', | ||
'docs' | ||
]); | ||
// Compile files and generate docs when something changes | ||
gulp.task('watch', [ | ||
'listen', | ||
'default' | ||
]); | ||
// Watch and reload | ||
// gulp watch | ||
exports.watch = series( | ||
exports.default, | ||
startServer, | ||
watchSource | ||
); |
{ | ||
"name": "gumshoejs", | ||
"version": "3.5.0", | ||
"description": "A simple, framework-agnostic scrollspy script.", | ||
"main": "./dist/js/gumshoe.min.js", | ||
"author": { | ||
"name": "Chris Ferdinandi", | ||
"url": "http://gomakethings.com" | ||
}, | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "http://github.com/cferdinandi/gumshoe" | ||
}, | ||
"devDependencies": { | ||
"del": "^2.2.0", | ||
"gulp": "^3.9.1", | ||
"gulp-concat": "^2.6.0", | ||
"gulp-file-include": "^0.14.0", | ||
"gulp-flatten": "^0.3.1", | ||
"gulp-footer": "^1.0.5", | ||
"gulp-header": "^1.8.8", | ||
"gulp-jshint": "^2.0.1", | ||
"gulp-livereload": "^3.8.1", | ||
"gulp-markdown": "^1.2.0", | ||
"gulp-optimize-js": "^1.0.2", | ||
"gulp-plumber": "^1.1.0", | ||
"gulp-rename": "^1.2.2", | ||
"gulp-tap": "^0.1.3", | ||
"gulp-uglify": "^2.0.0", | ||
"gulp-watch": "^4.3.9", | ||
"jshint": "^2.9.4", | ||
"jshint-stylish": "^2.2.1", | ||
"lazypipe": "^1.0.1", | ||
"node-fs": "^0.1.7" | ||
} | ||
"name": "gumshoejs", | ||
"version": "4.0.1", | ||
"description": "A simple, framework-agnostic scrollspy script.", | ||
"main": "./dist/js/gumshoe.min.js", | ||
"author": { | ||
"name": "Chris Ferdinandi", | ||
"url": "http://gomakethings.com" | ||
}, | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "http://github.com/cferdinandi/gumshoe" | ||
}, | ||
"devDependencies": { | ||
"gulp": "4.0.0", | ||
"del": "3.0.0", | ||
"lazypipe": "1.0.1", | ||
"gulp-flatmap": "1.0.2", | ||
"gulp-header": "2.0.5", | ||
"gulp-rename": "1.4.0", | ||
"jshint": "2.9.6", | ||
"gulp-jshint": "2.1.0", | ||
"jshint-stylish": "2.2.1", | ||
"gulp-concat": "2.6.1", | ||
"gulp-terser": "1.1.7", | ||
"gulp-optimize-js": "1.1.0", | ||
"gulp-sass": "4.0.2", | ||
"gulp-cssnano": "2.1.3", | ||
"gulp-autoprefixer": "6.0.0", | ||
"gulp-svgmin": "2.1.0", | ||
"browser-sync": "2.26.3" | ||
} | ||
} |
299
README.md
# Gumshoe [![Build Status](https://travis-ci.org/cferdinandi/gumshoe.svg)](https://travis-ci.org/cferdinandi/gumshoe) | ||
A simple, framework-agnostic scrollspy script. Gumshoe works great with [Smooth Scroll](https://github.com/cferdinandi/smooth-scroll). | ||
A simple vanilla JS scrollspy script. Gumshoe works great with [Smooth Scroll](https://github.com/cferdinandi/smooth-scroll). | ||
*See Gumshoe in action on Apple's [Swift.org website](https://swift.org/).* | ||
**[View the Demo on CodePen →](https://codepen.io/cferdinandi/pen/aMvxKr)** | ||
[Download Gumshoe](https://github.com/cferdinandi/gumshoe/archive/master.zip) / [View the demo](http://cferdinandi.github.io/gumshoe/) | ||
<hr> | ||
### Want to learn how to write your own vanilla JS plugins? Check out ["The Vanilla JS Guidebook"](https://gomakethings.com/vanilla-js-guidebook/) and level-up as a web developer. 🚀 | ||
### Want to learn how to write your own vanilla JS plugins? Check out my [Vanilla JS Pocket Guides](https://vanillajsguides.com/) or join the [Vanilla JS Academy](https://vanillajsacademy.com) and level-up as a web developer. 🚀 | ||
@@ -16,3 +14,2 @@ <hr> | ||
## Getting Started | ||
@@ -24,128 +21,304 @@ | ||
There are two versions of Gumshoe: the standalone version, and one that comes preloaded with polyfills for `closest()` and `CustomEvent()`, which are only supported in newer browsers. | ||
If you're including your own polyfills or don't want to enable this feature for older browsers, use the standalone version. Otherwise, use the version with polyfills. | ||
**Direct Download** | ||
You can [download the files directly from GitHub](https://github.com/cferdinandi/gumshoe/archive/master.zip). | ||
```html | ||
<script src="dist/js/gumshoe.js"></script> | ||
<script src="path/to/gumshoe.polyfills.min.js"></script> | ||
``` | ||
### 2. Add the markup to your HTML. | ||
**CDN** | ||
You can also use the [jsDelivr CDN](https://cdn.jsdelivr.net/gh/cferdinandi/gumshoe/dist/). I recommend linking to a specific version number or version range to prevent major updates from breaking your site. Gumshoe uses semantic versioning. | ||
```html | ||
<nav data-gumshoe-header> | ||
<ul data-gumshoe> | ||
<li class="active"><a class="active" href="#eenie">Eenie</a></li> | ||
<li><a href="#meanie">Meanie</a></li> | ||
<li><a href="#minnie">Minnie</a></li> | ||
<li><a href="#moe">Moe</a></li> | ||
</ul> | ||
</nav> | ||
<!-- Always get the latest version --> | ||
<!-- Not recommended for production sites! --> | ||
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/gumshoe/dist/gumshoe.polyfills.min.js"></script> | ||
<!-- Get minor updates and patch fixes within a major version --> | ||
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/gumshoe@4/dist/gumshoe.polyfills.min.js"></script> | ||
<!-- Get patch fixes within a minor version --> | ||
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/gumshoe@4.0/dist/gumshoe.polyfills.min.js"></script> | ||
<!-- Get a specific version --> | ||
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/gumshoe@4.0.0/dist/gumshoe.polyfills.min.js"></script> | ||
``` | ||
Add the `[data-gumshoe]` attribute to the navigation list that Gumshoe should watch. | ||
### 2. Add the markup to your HTML. | ||
If you're using a fixed header, add the `[data-gumshoe-header]` attribute and Gumshoe will automatically offset its calculations based on the header's height and distance from the top of the page. If you have multiple fixed headers, add `[data-gumshoe-header]` to the last one in the markup. | ||
The only thing Gumshoe needs to work is a list of anchor links. They can be ordered or unordered, inline or unstyled, or even nested. | ||
```html | ||
<ul id="my-awesome-nav"> | ||
<li><a href="#eenie">Eenie</a></li> | ||
<li><a href="#meenie">Meenie</a></li> | ||
<li><a href="#miney">Miney</a></li> | ||
<li><a href="#mo">Mo</a></li> | ||
</ul> | ||
``` | ||
### 3. Initialize Gumshoe. | ||
In the footer of your page, after the content, initialize Gumshoe by passing in a selector for the navigation links that should be detected as the user scrolls. | ||
```html | ||
<script> | ||
gumshoe.init(); | ||
var spy = new Gumshoe('#my-awesome-nav a'); | ||
</script> | ||
``` | ||
In the footer of your page, after the content, initialize Gumshoe. And that's it, you're done. Nice work! | ||
### 4. Add styling. | ||
Gumshoe adds the `.active` class to the list item (`<li></li>`) and content for the active link, but does not include any styling. | ||
Add styles to your CSS as desired. And that's it, you're done. Nice work! | ||
## Installing with Package Managers | ||
```css | ||
#my-awesome-nav a.active { | ||
font-weight: bold; | ||
} | ||
``` | ||
You can install Gumshoe with your favorite package manager. | ||
**[View a Demo on CodePen →](https://codepen.io/cferdinandi/pen/aMvxKr)** | ||
* **NPM:** `npm install cferdinandi/gumshoe` | ||
* **Bower:** `bower install https://github.com/cferdinandi/gumshoe.git` | ||
* **Component:** `component install cferdinandi/gumshoe` | ||
*__Note:__ you can customize the class names with [user options](#options-and-settings).* | ||
## Working with the Source Files | ||
## Nested navigation | ||
If you would prefer, you can work with the development code in the `src` directory using the included [Gulp build system](http://gulpjs.com/). This compiles, lints, and minifies code. | ||
If you have a nested navigation menu with multiple levels, Gumshoe can also apply an `.active` class to the parent list items of the currently active link. | ||
### Dependencies | ||
Make sure these are installed first. | ||
```html | ||
<ul id="my-awesome-nav"> | ||
<li><a href="#eenie">Eenie</a></li> | ||
<li> | ||
<a href="#meenie">Meenie</a> | ||
<ul> | ||
<li><a href="#hickory">Hickory</a></li> | ||
<li><a href="#dickory">Dickory</a></li> | ||
<li><a href="#doc">Doc</a></li> | ||
</ul> | ||
</li> | ||
<li><a href="#miney">Miney</a></li> | ||
<li><a href="#mo">Mo</a></li> | ||
</ul> | ||
``` | ||
* [Node.js](http://nodejs.org) | ||
* [Gulp](http://gulpjs.com) `sudo npm install -g gulp` | ||
Set `nested` to `true` when instantiating Gumshoe. You can also customize the class name. | ||
### Quick Start | ||
```js | ||
var spy = new Gumshoe('#my-awesome-nav a', { | ||
nested: true, | ||
nestedClass: 'active-parent' | ||
}); | ||
``` | ||
1. In bash/terminal/command line, `cd` into your project directory. | ||
2. Run `npm install` to install required files. | ||
3. When it's done installing, run one of the task runners to get going: | ||
* `gulp` manually compiles files. | ||
* `gulp watch` automatically compiles files and applies changes using [LiveReload](http://livereload.com/). | ||
**[Try nested navigation on CodePen →](https://codepen.io/cferdinandi/pen/JzYVxj)** | ||
## Catching reflows | ||
## Options and Settings | ||
If the content that's linked to by your navigation has different layouts at different viewports, Gumshoe will need to detect these changes and update some calculations behind-the-scenes. | ||
Set `reflow` to `true` to enable this (it's off by default). | ||
```js | ||
var spy = new Gumshoe('#my-awesome-nav a', { | ||
reflow: true | ||
}); | ||
``` | ||
## Accounting for fixed headers | ||
If you have a fixed header on your page, you may want to offset when a piece of content is considered "active." | ||
The `offset` user setting accepts either a number, or a function that returns a number. If you need to dynamically calculate dimensions, a function is the preferred method. | ||
Here's an example that automatically calculates a header's height and offsets by that amount. | ||
```js | ||
// Get the header | ||
var header = document.querySelector('#my-header'); | ||
// Initialize Gumshoe | ||
var spy = new Gumshoe('#my-awesome-nav a', { | ||
offset: function () { | ||
return header.getBoundingClientRect().height; | ||
} | ||
}); | ||
``` | ||
**[Try using an offset on CodePen →](https://codepen.io/cferdinandi/pen/eXpLqo)** | ||
## API | ||
Gumshoe includes smart defaults and works right out of the box. But if you want to customize things, it also has a robust API that provides multiple ways for you to adjust the default options and settings. | ||
### Global Settings | ||
### Options and Settings | ||
You can pass options and callbacks into Gumshoe through the `init()` function: | ||
You can pass options into Gumshoe when instantiating. | ||
```javascript | ||
gumshoe.init({ | ||
selector: '[data-gumshoe] a', // Default link selector (must use a valid CSS selector) | ||
selectorHeader: '[data-gumshoe-header]', // Fixed header selector (must use a valid CSS selector) | ||
container: window, // The element to spy on scrolling in (must be a valid DOM Node) | ||
offset: 0, // Distance in pixels to offset calculations | ||
activeClass: 'active', // Class to apply to active navigation link and its parent list item | ||
scrollDelay: false, // Wait until scrolling has stopped before updating the navigation | ||
callback: function (nav) {} // Callback to run after setting active link | ||
var spy = new Gumshoe('#my-awesome-nav a', { | ||
// Active classes | ||
navClass: 'active', // applied to the nav list item | ||
contentClass: 'active', // applied to the content | ||
// Nested navigation | ||
nested: false, // if true, add classes to parents of active link | ||
nestedClass: 'active', // applied to the parent items | ||
// Offset & reflow | ||
offset: 0, // how far from the top of the page to activate a content area | ||
reflow: false, // if true, listen for reflows | ||
// Event support | ||
events: true // if true, emit custom events | ||
}); | ||
``` | ||
***Note:*** *The `scrollDelay` option can be useful in preventing the elements of your navigation from being highlighted and unhighlighted in rapid succession when quickly scrolling (e.g., with [Smooth Scroll](https://github.com/cferdinandi/smooth-scroll)) through a page with many navigation items (e.g. a long document with a table of contents in the sidebar).* | ||
### Custom Events | ||
### Use Gumshoe events in your own scripts | ||
Gumshoe emits two custom events: | ||
You can also call Gumshoe events in your own scripts. | ||
- `gumshoeActivate` is emitted when a link is activated. | ||
- `gumshoeDeactivate` is emitted when a link is deactivated. | ||
#### setDistances() | ||
Recalculate the height of document, the height of the fixed header, and how far navigation targets are from the top of the document. | ||
Both events are emitted on the list item and bubble up. You can listen for them with the `addEventListener()` method. The `event.detail` object includes the `link` and `content` elements, and the `settings` for the current instantiation. | ||
```js | ||
// Listen for activate events | ||
document.addEventListener('gumshoeActivate', function (event) { | ||
// The list item | ||
var li = event.target; | ||
// The link | ||
var link = event.detail.link; | ||
// The content | ||
var content = event.detail.content; | ||
}, false); | ||
``` | ||
### Methods | ||
Gumshoe also exposes several public methods. | ||
#### setup() | ||
Setups all of the calculations Gumshoe needs behind-the-scenes. If you dynamically add navigation items to the DOM after Gumshoe is instantiated, you can run this method to update the calculations. | ||
**Example** | ||
```javascript | ||
gumshoe.setDistances(); | ||
var spy = new Gumshoe('#my-awesome-nav a'); | ||
spy.setup(); | ||
``` | ||
#### getCurrentNav() | ||
Determine which navigation element is currently active and add active classes. | ||
#### detect() | ||
Activate the navigation link that's content is currently in the viewport. | ||
**Example** | ||
```javascript | ||
gumshoe.getCurrentNav(); | ||
var spy = new Gumshoe('#my-awesome-nav a'); | ||
spy.detect(); | ||
``` | ||
#### destroy() | ||
Destroy the current `gumshoe.init()`. This is called automatically during the init function to remove any existing initializations. | ||
Destroy the current instantiation of Gumshoe. | ||
**Example** | ||
```javascript | ||
gumshoe.destroy(); | ||
var spy = new Gumshoe('#my-awesome-nav a'); | ||
spy.destroy(); | ||
``` | ||
#### init() | ||
Reinitialize Gumshoe. This is called automatically when you instantiate your `new Gumshoe` object, but can be used to reinitialize your instance after running `destroy()`. | ||
**Example** | ||
```javascript | ||
var spy = new Gumshoe('#my-awesome-nav a'); | ||
spy.destroy(); | ||
// Some time later... | ||
spy.init({ | ||
// New options | ||
}); | ||
``` | ||
## Working with the Source Files | ||
If you would prefer, you can work with the development code in the `src` directory using the included [Gulp build system](http://gulpjs.com/). This compiles, lints, and minifies code. | ||
### Dependencies | ||
Make sure these are installed first. | ||
* [Node.js](http://nodejs.org) | ||
* [Gulp](http://gulpjs.com) `sudo npm install -g gulp` | ||
### Quick Start | ||
1. In bash/terminal/command line, `cd` into your project directory. | ||
2. Run `npm install` to install required files. | ||
3. When it's done installing, run one of the task runners to get going: | ||
* `gulp` manually compiles files. | ||
* `gulp watch` automatically compiles files when changes are made and applies changes using [LiveReload](http://livereload.com/). | ||
## Migrating to Gumshoe 4 from Older Versions | ||
Gumshoe 4 is a ground-up rewrite. | ||
### New Features | ||
- Multiple instantiations can be run with different settings for each. | ||
- An active class is now added to the content as well. | ||
- Nested navigation is now supported. | ||
- Offsets can be dynamically calculated instead of set just once at initialization. | ||
- Special and non-Roman characters can now be used in anchor links and IDs. | ||
- Custom events provide a more flexible way to react to DOM changes. | ||
### Breaking Changes | ||
- Gumshoe must now be instantiated as a new object (`new Gumshoe()`) instead of being initialized `gumshoe.init()`. | ||
- Callback methods have been removed in favor of events. | ||
- Automatic header offsetting has been removed. | ||
## Browser Compatibility | ||
Gumshoe works in all modern browsers, and IE 10 and above. You can extend browser support back to IE 9 with the [classList.js polyfill](https://github.com/eligrey/classList.js/). | ||
Gumshoe works in all modern browsers, and IE 9 and above. | ||
### Polyfills | ||
Support back to IE9 requires polyfills for `closest()` and `CustomEvent()`. Without them, support starts with Edge. | ||
## How to Contribute | ||
Use the included polyfills version of Gumshoe, or include your own. | ||
Please review the [contributing guidelines](CONTRIBUTING.md). | ||
## License | ||
The code is available under the [MIT License](LICENSE.md). |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
75390
17
23
323
0
1335
1