Skip to content

Commit

Permalink
add Event.scrollend polyfill (#30)
Browse files Browse the repository at this point in the history
This is roughly based on : https://github.com/argyleink/scrollyfills
So also attributed to and licensed as this original work.

The original however was slightly broken and didn't implement the `once`
option.
It also lacked tests.

---------

Co-authored-by: Simon Menke <simon.menke@gmail.com>
  • Loading branch information
romainmenke and fd authored Apr 8, 2024
1 parent 4c6bc06 commit d890f46
Show file tree
Hide file tree
Showing 4 changed files with 521 additions and 0 deletions.
40 changes: 40 additions & 0 deletions polyfills/Event/scrollend/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#### Aliases
aliases = []

#### Dependencies
dependencies = [
"Event",
"Set",
"WeakMap",
"smoothscroll"
]

#### Specification Link
spec = "https://html.spec.whatwg.org/multipage/webappapis.html#handler-onscrollend"

#### Documentation Link
docs = "https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollend_event"

#### License
license = "ISC"

# The polyfill code was ported to ES3 for compatibility with older browsers.
repo = "https://github.com/argyleink/scrollyfills"

#### Browser compatibility
[browsers]
android = "*"
bb = "*"
chrome = "<114"
edge = "*"
edge_mob = "*"
firefox = "<109"
firefox_mob = "<109"
ie = "*"
ie_mob = "*"
opera = "<100"
op_mob = "<76"
op_mini = "*"
safari = "*"
ios_saf = "*"
samsung_mob = "<23.0"
1 change: 1 addition & 0 deletions polyfills/Event/scrollend/detect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'onscrollend' in self && (self.onscrollend == null || typeof self.onscrollend === 'function')
132 changes: 132 additions & 0 deletions polyfills/Event/scrollend/polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
(function (global) {
var scrollEndEvent = new global.Event('scrollend');
var pointers = new global.Set();

// Track if any pointer is active
global.document.addEventListener('touchstart', function (e) {
for (var i = 0; i < e.changedTouches.length; i++) {
pointers.add(e.changedTouches[i].identifier);
}
}, { passive: true });

global.document.addEventListener('touchend', function (e) {
for (var i = 0; i < e.changedTouches.length; i++) {
pointers.delete(e.changedTouches[i].identifier);
}
}, { passive: true });

// Map of scroll-observed elements.
var observed = new global.WeakMap();

function onAddListener(originalFn, type) {
var scrollPort = this;

var data = observed.get(scrollPort);
if (data !== undefined) {
data.listeners[type]++;
return;
}

var timeout = 0;

data = {
scrollListener: function scrollListener(evt) { // eslint-disable-line no-unused-vars
clearTimeout(timeout);

timeout = setTimeout(function () {
if (pointers.size) {
// if pointer(s) are down, wait longer
setTimeout(data.scrollListener, 100);
return;
}

// dispatch
if (scrollPort) {
scrollPort.dispatchEvent(scrollEndEvent);
}
}, 100);

},
listeners: {
scroll: 0,
scrollend: 0
}
};

originalFn.apply(scrollPort, ['scroll', data.scrollListener]);
observed.set(scrollPort, data);
}

function onRemoveListener(originalFn, type) {
var scrollPort = this;
var data = observed.get(scrollPort);

if (data === undefined) {
return;
}

data.listeners[type] = Math.max(0, data.listeners[type] - 1);
// If there are still listeners, nothing more to do.
if ((data.listeners.scroll + data.listeners.scrollend) > 0) {
return;
}

// Otherwise, remove the added listeners.
originalFn.apply(scrollPort, ['scroll', data.scrollListener]);
observed.delete(scrollPort);
}

function polyfillAddEventListener(proto) {
var native = proto.addEventListener;
proto.addEventListener = function addEventListener(type, callback) { // eslint-disable-line no-unused-vars
var args = global.Array.prototype.slice.apply(arguments, [0]);
native.apply(this, args);

if (type !== 'scroll' && type !== 'scrollend') {
return;
}

if (arguments[2] && arguments[2].once) {
var removerArgs = global.Array.prototype.slice.apply(arguments, [0]);
var _this = this;

var remover = function () {
_this.removeEventListener(type, remover);
_this.removeEventListener.apply(_this, removerArgs);
}

_this.addEventListener(type, remover);
}

args.unshift(native);
onAddListener.apply(this, args);
}
}

function polyfillRemoveEventListener(proto) {
var native = proto.removeEventListener;
proto.removeEventListener = function removeEventListener(type, callback) { // eslint-disable-line no-unused-vars
var args = global.Array.prototype.slice.apply(arguments, [0]);
native.apply(this, args);

if (type !== 'scroll' && type !== 'scrollend') {
return;
}

args.unshift(native);
onRemoveListener.apply(this, args);
}
}

polyfillAddEventListener(global.Element.prototype);
polyfillAddEventListener(global);
polyfillAddEventListener(global.document);
polyfillRemoveEventListener(global.Element.prototype);
polyfillRemoveEventListener(global);
polyfillRemoveEventListener(global.document);

if (!('onscrollend' in self)) {
// Make sure a check for 'onhashchange' in window will pass
global.onscrollend = null;
}
}(self));
Loading

0 comments on commit d890f46

Please sign in to comment.