scroll-scope.js

Small jQuery plugin to keep parent element still when scrolling an element to its boundary.

Commonly in scroll interaction, user hovers a mouse cursor over a scrollable element and uses trackpad or mouse wheel to scroll the element. When an element reaches its boundary, its parent element continues scrolling. Usually this means that the user will continue moving down the page when attempting to interact with an specific container. This is a common issue with dropdown menus and modal dialogs.

This behavior is most prevalent in desktop browsers. To fix this problem that shouldn't even exist, scroll-scope.js exists.

See project home and demos on eiskis.net/scroll-scope.

Get the plugin

Current version is 0.1.0.

Install with Bower:

bower install scroll-scope

File issues or pull requests to share potential improvements. The plugin is released under MIT. Source is available on GitHub. To contribute, clone the repo, make changes in scroll-scope.js and build the minified version with gulp.

Usage

Add the data-scroll-scope attribute to any scrollable element on the page:

<!-- Scope scrolling of element when it overflows -->
<div class="my-scrollable-element" data-scroll-scope>

<!-- Scope scrolling of element whether or not it overflows -->
<div class="another-scrollable-element" data-scroll-scope="force">

Include and initialize:

<!-- jQuery comes first, then the plugin -->
<script type="text/javascript" src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="scroll-scope.min.js"></script>

<!-- Activate the plugin on your page -->
<script type="text/javascript">
    $(document).scrollScope();
</script>

The plugin works declaratively, meaning that it's attached to the document object (or any parent container you choose) instead of individual scrollable containers. This means that any DOM elements that are added or removed after page load do not need to be bound separately. You can even toggle force on and off during runtime if you please.

Options

You can change which elements and events are targeted by setting them upon initialization. Note that having events listed here does not mean they're blocked automatically, rather the plugin listens to these events and evaluates them when encountered.

Here are the defaults:

$(document).scrollScope({
    elements: '[data-scroll-scope]',
    forcedElements: '[data-scroll-scope="force"]',
    events: 'DOMMouseScroll mousewheel scroll touchstart touchmove'
});

Advanced use

If you need full access to ScrollScope object (for example to getTargetedElements() or unbind() it later), instantiate a raw ScrollScope object yourself. You need to bind it to the document yourself in this case:

// Create new instance
var myScrollScopeInstance = new ScrollScope({
    options: 'here'
});

// Bind to document
myScrollScopeInstance.bind(document);

// Use the object for whatever you wish
myScrollScopeInstance.mainContainer.css('background', 'blue');
myScrollScopeInstance.getTargetedElements().css('background', 'red');

// Detach from document if no longer needed
myScrollScope.unbind();

Credits

Plugin by Jerry Jäppinen (under MIT).

Demos

Default behavior

data-scroll-scope

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis.

When scrolling the container on the right, scrolling should stop when you get to the end and your document should stay still. For some browsers, this is also the default behavior.

Also keep in mind that especially mobile browsers allow the user to keep scrolling the parent if the child is at its respective boundary when initiating the scroll. If you want to block scrolling even in these cases, use force (see modal dialog example below).

Real-life use case: dropdown

The document will stay still even when you scroll the results container to the end.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Real-life use case: code block

/*A page with lots of code blocks could set a max-height for them*/
pre {
	overflow: auto;
	max-height: 16em;
}

/*Lots of code*/
code {
	display: inline-block;
	color: #555;
	background-color: #f6f6f6;
	border-radius: 3px;
	padding-left: 0.3em;
	padding-right: 0.3em;
}
pre code {
	display: block;
	padding: 1em;
	overflow: auto;
}

Real-life use case: modal dialog

Modal dialog implementations tend to scroll the document. Even Bootstrap's modal dialog overlay lets the scroll event through on mobile Safari!

Open the demo dialog

In this quite trivial custom dialog implementation, we scope the scrolling in both the overall container and the content area so the document maintains its position. We also want to use force to disable parent scrolling even when the areas do not overflow.

Note! When scroll events are blocked with force, mobile Safari also blocks click events for that element. To close the modal on overlay click, we must attach the click event handler to an element that does not use data-scroll-scope.

The source looks like this:

<div class="modal" data-scroll-scope="force">
	<div class="modal-content" data-scroll-scope="force">
		...
	</div>
	<div class="modal-overlay" data-action="toggle-modal"></div>
</div>
// Quick custom toggle
$(document).on('click', '[data-action="toggle-modal"]', function (event) {
	event.preventDefault();
	$('.modal').toggleClass('closed');
});
.modal {
	position: fixed;
	z-index: 100;
	width: 100%;
	height: 100%;
	left: 0;
	top: 0;
	overflow: hidden;
}
	.modal.closed {
		display: none;
	}
.modal-overlay {
	position: fixed;
	z-index: 1;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
	position: fixed;
	z-index: 2;
	overflow: auto;
	top: 5%;
	left: 5%;
	width: 90%;
	height: 30em;
	max-height: 90%;
	background-color: #fff;
}

Nested containers

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Impractical number of nested containers

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia aliquam, nemo molestiae consequatur officiis magni eos aliquid incidunt perspiciatis. Laudantium dolorum reprehenderit corporis dignissimos eaque, possimus quam, sequi ab soluta.