Skip to content

Commit

Permalink
Merge pull request #53 from stutrek/dom-element-containers
Browse files Browse the repository at this point in the history
Allow DOM element containers
  • Loading branch information
stutrek authored Dec 1, 2016
2 parents 37d1061 + 4af36c0 commit c054aab
Show file tree
Hide file tree
Showing 24 changed files with 1,539 additions and 955 deletions.
52 changes: 52 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"parser": "babel-eslint",
"extends": "eslint:recommended",
"env": {
"browser": true,
"node": true,
"amd": true,
"commonjs": true,
"es6": true
},
"globals": {
"app": true,
"PROD": false,
"DEV": false
},
"parserOptions": {
"ecmaFeatures": {
"modules": true,
"jsx": true
},
"ecmaVersion": 6,
"sourceType": "module",
"jsx": true
},
"plugins": [
"react"
],
"rules": {
"eqeqeq": 2,
"quotes": [1, "single"],
"no-console": 1,
"dot-location": [1, "property"],
"no-caller": 2,
"no-fallthrough": 2,
"no-floating-decimal": 1,
"no-loop-func": 0,
"no-new-func": 2,
"no-sequences": 1,
"no-throw-literal": 2,
"no-label-var": 2,
"no-shadow-restricted-names": 2,
"no-shadow": 2,
"no-use-before-define": 2,
"react/jsx-uses-vars": 2,
"camelcase": 1,
"no-lonely-if": 1,
"no-nested-ternary": 2,
"no-unneeded-ternary": 1,
"no-trailing-spaces": 1,
"semi": 2
}
}
44 changes: 40 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
scrollMonitor
=============

The scroll monitor allows you to receive events when elements enter or exit the viewport. It does this using watcher objects, which watch an element and trigger events. Watcher objects also contain information about the element they watch, including the element's visibility and location relative to the viewport.
The scroll monitor allows you to receive events when elements enter or exit a viewport. It does this using watcher objects, which watch an element and trigger events. Watcher objects also contain information about the element they watch, including the element's visibility and location relative to the viewport. If your scroll container is an element other than the body you can create a container that creates watchers.

The scroll monitor was designed to be very fast. On each scroll event the DOM is only touched twice, once to find the document height and again to find the viewport top. No variables are declared, nor are any objects, arrays, or strings created.

The code is based on vanilla javascript and has no external dependencies. Except if you want to put it in the head of the document, then you'll need jQuery for the DOMContentLoaded event.
The code is based on vanilla javascript and has no external dependencies. _The script cannot be put in the head_.

Watchers are _very_ cheap. Create them liberally.

[![browser support](http://ci.testling.com/sakabako/scrollMonitor.png)](http://ci.testling.com/sakabako/scrollMonitor)

## Basic Usage

### When the body scrolls

```javascript
var scrollMonitor = require("./scrollMonitor"); // if you're not using require, you can use the scrollMonitor global.
var myElement = document.getElementById("itemToWatch");
Expand All @@ -26,9 +26,34 @@ elementWatcher.exitViewport(function() {
console.log( 'I have left the viewport' );
});
```

### For a scroll container

```javascript
var containerElement = document.getElementById("container");

var containerMonitor = scrollMonitor.createContainer(containerElement);
// this containerMonitor is an instance of the scroll monitor
// that listens to scroll events on your container.

var childElement = document.getElementById("child-of-container");
var elementWatcher = containerMonitor.create(childElement);

elementWatcher.enterViewport(function() {
console.log( 'I have entered the viewport' );
});
elementWatcher.exitViewport(function() {
console.log( 'I have left the viewport' );
});
```

_Note: an element is said to be in the viewport if it is scrolled into its parent, it does not matter if the parent is in the viewport._

## Demos

* [Stress Test](http://stutrek.github.com/scrollMonitor/demos/stress.html) - Test with as many watchers as you'd like
* [Stress Test in a div](http://stutrek.github.com/scrollMonitor/demos/stressTestInADiv.html) - Note how much slower scrolling a div is than scrolling the body.
* [Nested scrollers](http://stutrek.github.com/scrollMonitor/demos/divInADiv.html)
* [Fixed Positioning and Locking](http://stutrek.github.com/scrollMonitor/demos/fixed.html)
* [Anchored section headers](http://stutrek.github.com/scrollMonitor/demos/list.html)
* [Complex sidebar behavior](http://stutrek.github.com/scrollMonitor/demos/scoreboard.html)
Expand Down Expand Up @@ -134,6 +159,7 @@ scrollMonitor.create( element, -200 )
## scrollMonitor Module

### Methods
* `scrollMonitor.createContainer( containerEl )` - returns a new ScrollMonitorContainer that can be used just like the scrollMonitor module.
* `scrollMonitor.create( watchItem, offsets )` - Returns a new watcher. `watchItem` is a DOM element, jQuery object, NodeList, CSS selector, object with .top and .bottom, or a number.
* `scrollMonitor.update()` - update and trigger all watchers.
* `scrollMonitor.recalculateLocations()` - recalculate the location of all unlocked watchers and trigger if needed.
Expand All @@ -144,3 +170,13 @@ scrollMonitor.create( element, -200 )
* `scrollMonitor.viewportHeight` - height of the viewport.
* `scrollMonitor.documentHeight` - height of the document.

# Contributing

There is a set of unit tests written with Mocha + Chai that run in testem. Getting them running is simple:

```
npm install
npm test
```

then open http://localhost:7357
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "scrollMonitor",
"version": "1.1.3",
"version": "1.2.0",
"main": "./scrollMonitor.js",
"dependencies": {},
"devDependencies": {},
Expand Down
135 changes: 135 additions & 0 deletions demos/divInADiv.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<!doctype html>
<head>
<style type="text/css">
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, sans-serif;
}
.screen {
height: 100vh;
width: 100%;
position: relative;
min-height: 900px;
}
.content {
position: absolute;
height: 400px;
width: 615px;
padding: 15px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: auto;
background-color: white;
}
.content img {
width: 100%;
}
.one {
background-color: silver;
}
.two {
background-color: #c00;
}
.three {
background-color: #00c;
}
h1 {
margin-top: 0;
}
.insert-boxes-here {
margin: 0 -5px;
}
.box {
display: inline-block;
background-color: #ccc;
width: 75px;
height: 75px;
margin: 5px;
line-height: 75px;
text-align: center;
color: #999;
/*border: 1px solid black;*/
}
.box.in {
background-color: #fcc;
}
.box.partial-above {
background-color: #ccf;
}
.box.partial-below {
background-color: #ffc;
}

.internal {
height: 300px;
margin: 0 73px 10px;
text-align: center;
background-color: #aaa;
}

.scroller {
overflow-y: auto;
}
</style>
</head>
<body>
<div>
<div class="screen one">
<div class="content scroller">
<h1>Here are a couple of boxes from the stress test.</h1>
<div class="insert-boxes-here"></div>
</div>
</div>
<div class="screen two">
<div class="content scroller">
<h1>And a few more, just to prove that it works down here too.</h1>
<div class="insert-boxes-here"></div>
</div>
</div>
<div class="screen three">
<div class="content scroller">
<h1>And a scroller in a scroller.</h1>
<div class="internal scroller">
<h2>Just for you.</h2>
<div class="insert-boxes-here"></div>
</div>
<div class="insert-boxes-here"></div>
</div>
</div>
</div>
<a href="https://github.com/sakabako/scrollMonitor"><img style="position: fixed; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="../scrollMonitor.js"></script>
<script type="text/javascript">
var boxHtml = (new Array(100)).join('<span class="box"></span>');
$('.insert-boxes-here').html(boxHtml);

function listener(event, watcher) {
if (!watcher.isInViewport) {
return;
} else if (watcher.isFullyInViewport) {
watcher.watchItem.style.backgroundColor = '#fcc';
} else if (watcher.isAboveViewport) {
watcher.watchItem.style.backgroundColor = '#ccf';
} else if (watcher.isBelowViewport) {
watcher.watchItem.style.backgroundColor = '#ffc';
}
}

$('.scroller').each(function (i, element) {
var boxes = $('> .insert-boxes-here > .box', element);
var container = scrollMonitor.createContainer(element);
boxes.each(function (i, boxEl) {
var watcher = container.create(boxEl);
watcher.stateChange(listener);
listener(null, watcher);
});
});

</script>
</body>
9 changes: 5 additions & 4 deletions demos/fixed.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<script src="../scrollMonitor.js"></script>
<script type="text/javascript">

$('span').each(function(i, element) {
var watchers = $('span').map(function(i, element) {

var watcher = scrollMonitor.create( element );

Expand All @@ -49,8 +49,9 @@
watcher.stateChange(function() {
$(element).toggleClass('fixed', this.isAboveViewport)
});

})
return watcher;
});
console.log(watchers);

</script>
</body>
</body>
Loading

0 comments on commit c054aab

Please sign in to comment.