Skip to content

Commit

Permalink
Support a tree of scrollable elements (#11)
Browse files Browse the repository at this point in the history
* Refactor to support a tree of scrollable elements.

* Update testing page.

* Upgrade version number.
  • Loading branch information
calvellido committed Feb 14, 2020
1 parent b07b595 commit d07c4a2
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 21 deletions.
66 changes: 53 additions & 13 deletions index.html
Expand Up @@ -18,17 +18,49 @@
<header>
<h1>focus-options-polyfill</h1>
</header>
<article>
<article style="height: 2000px; width: 500px; display: inline-block; vertical-align: top;">

<button type="button" onclick="focusScrollMethod()">
Click me to focus on the button!
<button type="button" onclick="focusScrollMethod('focusable-button-1')">
Click me to focus on the button 1
</button>
<button type="button" onclick="focusNoScrollMethod()">
Click me to focus on the button without scrolling!
<button type="button" onclick="focusNoScrollMethod('focusable-button-1')">
Click me to focus on the button 1 without scrolling
</button>

<div id="container" style="height: 2000px;">
<button type="button" id="focusable-button" style="margin-top: 1500px;">Click Me!</button>
<div class="container">
<button type="button" id="focusable-button-1" style="margin-top: 1500px;">Button 1</button>
</div>

</article>

<article style="height: 500px; width: 500px; display: inline-block; vertical-align: top; overflow: auto;">

<button type="button" onclick="focusScrollMethod('focusable-button-2')">
Click me to focus on the button 2
</button>
<button type="button" onclick="focusNoScrollMethod('focusable-button-2')">
Click me to focus on the button 2 without scrolling
</button>

<div class="container">
<button type="button" id="focusable-button-2" style="margin: 600px 0 0 600px; white-space: nowrap">Button 2</button>
</div>

</article>

<article style="height: 300px; width: 500px; display: inline-block; vertical-align: top; overflow: auto;">

<button type="button" onclick="focusScrollMethod('focusable-button-3')">
Click me to focus on the button 3
</button>
<button type="button" onclick="focusNoScrollMethod('focusable-button-3')">
Click me to focus on the button 3 without scrolling
</button>

<div class="container" style="height: 500px; width: 300px; overflow: auto;">
<div class="container" style="margin: 300px 0 0 300px;">
<button type="button" id="focusable-button-3" style="margin: 300px 0 0 300px; white-space: nowrap">Button 3</button>
</div>
</div>

</article>
Expand All @@ -40,16 +72,24 @@ <h1>focus-options-polyfill</h1>
</footer>

<script>
focusScrollMethod = function getFocus() {
document.getElementById("focusable-button").focus({ preventScroll: false });
focusScrollMethod = function getFocus(id) {
document.getElementById(id).focus({ preventScroll: false });
}

focusNoScrollMethod = function getFocusWithoutScrolling() {
document.getElementById("focusable-button").focus({ preventScroll: true });
focusNoScrollMethod = function getFocusWithoutScrolling(id) {
document.getElementById(id).focus({ preventScroll: true });
}

document.getElementById("focusable-button").addEventListener("click", function() {
alert("Button clicked!")
document.getElementById("focusable-button-1").addEventListener("click", function() {
alert("Button 1 clicked!")
});

document.getElementById("focusable-button-2").addEventListener("click", function() {
alert("Button 2 clicked!")
});

document.getElementById("focusable-button-3").addEventListener("click", function() {
alert("Button 3 clicked!")
});
</script>

Expand Down
44 changes: 37 additions & 7 deletions index.js
Expand Up @@ -34,15 +34,45 @@
) {
HTMLElement.prototype.nativeFocus = HTMLElement.prototype.focus;

var calcScrollableElements = function(element) {
var parent = element.parentNode;
var scrollableElements = [];

while (parent && parent !== document.scrollingElement) {
if (
parent.offsetHeight < parent.scrollHeight ||
parent.offsetWidth < parent.scrollWidth
) {
scrollableElements.push([
parent,
parent.scrollTop,
parent.scrollLeft
]);
}
parent = parent.parentNode;
}
parent = document.scrollingElement;
scrollableElements.push([parent, parent.scrollTop, parent.scrollLeft]);

return scrollableElements;
};

var restoreScrollPosition = function(scrollableElements) {
for (var i = 0; i < scrollableElements.length; i++) {
scrollableElements[i][0].scrollTop = scrollableElements[i][1];
scrollableElements[i][0].scrollLeft = scrollableElements[i][2];
}
scrollableElements = [];
};

var patchedFocus = function(args) {
var actualPosition = window.scrollY || window.pageYOffset;
this.nativeFocus();
if (args && args.preventScroll) {
// Hijacking the event loop order, since the focus() will trigger
// internally an scroll that goes to the event loop
setTimeout(function() {
window.scroll(window.scrollX || window.pageXOffset, actualPosition);
}, 0);
var evScrollableElements = calcScrollableElements(this);
this.nativeFocus();
restoreScrollPosition(evScrollableElements);
}
else {
this.nativeFocus();
}
};

Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "focus-options-polyfill",
"version": "1.2.0",
"version": "1.3.0",
"description": "JavaScript polyfill for the WHATWG spec of focusOptions, that enables a set of options to be passed to the focus method.",
"main": "index.js",
"scripts": {
Expand Down

0 comments on commit d07c4a2

Please sign in to comment.