Created
January 1, 2025 03:27
-
-
Save GLWalker/6204e526cea406e6afc37c8b4746b115 to your computer and use it in GitHub Desktop.
Wow JS Modernized
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;(function (global, factory) { | |
if (typeof define === "function" && define.amd) { | |
define(["module", "exports"], factory) | |
} else if (typeof exports !== "undefined") { | |
factory(module, exports) | |
} else { | |
const mod = { exports: {} } | |
factory(mod, mod.exports) | |
global.WOW = mod.exports | |
} | |
})(this, function (module, exports) { | |
"use strict" | |
// Use ES6+ syntax | |
const isIn = (needle, haystack) => haystack.includes(needle) | |
const extend = (custom, defaults) => { | |
return { ...defaults, ...custom } | |
} | |
const isMobile = (agent) => | |
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( | |
agent | |
) | |
const createEvent = ( | |
event, | |
bubble = false, | |
cancel = false, | |
detail = null | |
) => { | |
const customEvent = new CustomEvent(event, { | |
bubbles: bubble, | |
cancelable: cancel, | |
detail, | |
}) | |
return customEvent | |
} | |
const emitEvent = (elem, event) => { | |
elem.dispatchEvent(event) | |
} | |
const addEvent = (elem, event, fn) => { | |
elem.addEventListener(event, fn, false) | |
} | |
const removeEvent = (elem, event, fn) => { | |
elem.removeEventListener(event, fn, false) | |
} | |
const getInnerHeight = () => | |
window.innerHeight || document.documentElement.clientHeight | |
// MutationObserver is natively supported in modern browsers | |
const MutationObserver = | |
window.MutationObserver || | |
window.WebkitMutationObserver || | |
window.MozMutationObserver | |
const WOW = class { | |
constructor(options = {}) { | |
this.defaults = { | |
boxClass: "wow", | |
animateClass: "animated", | |
offset: 0, | |
mobile: true, | |
live: true, | |
callback: null, | |
scrollContainer: null, | |
resetAnimation: true, | |
} | |
this.animate = window.requestAnimationFrame | |
? (callback) => window.requestAnimationFrame(callback) | |
: (callback) => callback() | |
this.vendors = ["moz", "webkit"] | |
this.config = { ...this.defaults, ...options } | |
if (options.scrollContainer) { | |
this.config.scrollContainer = document.querySelector( | |
options.scrollContainer | |
) | |
} | |
this.animationNameCache = new WeakMap() | |
this.wowEvent = createEvent(this.config.boxClass) | |
this.start = this.start.bind(this) | |
this.resetAnimation = this.resetAnimation.bind(this) | |
this.scrollHandler = this.scrollHandler.bind(this) | |
this.scrollCallback = this.scrollCallback.bind(this) | |
} | |
init() { | |
this.element = document.documentElement | |
if (["interactive", "complete"].includes(document.readyState)) { | |
this.start() | |
} else { | |
addEvent(document, "DOMContentLoaded", this.start) | |
} | |
this.finished = [] | |
} | |
start() { | |
this.stopped = false | |
this.boxes = Array.from( | |
this.element.querySelectorAll(`.${this.config.boxClass}`) | |
) | |
this.all = [...this.boxes] | |
if (this.boxes.length) { | |
if (this.disabled()) { | |
this.resetStyle() | |
} else { | |
this.boxes.forEach((box) => this.applyStyle(box, true)) | |
} | |
} | |
if (!this.disabled()) { | |
addEvent( | |
this.config.scrollContainer || window, | |
"scroll", | |
this.scrollHandler | |
) | |
addEvent(window, "resize", this.scrollHandler) | |
this.interval = setInterval(this.scrollCallback, 50) | |
} | |
if (this.config.live) { | |
const mut = new MutationObserver((records) => { | |
records.forEach((record) => { | |
record.addedNodes.forEach((node) => this.doSync(node)) | |
}) | |
}) | |
mut.observe(document.body, { childList: true, subtree: true }) | |
} | |
} | |
stop() { | |
this.stopped = true | |
removeEvent( | |
this.config.scrollContainer || window, | |
"scroll", | |
this.scrollHandler | |
) | |
removeEvent(window, "resize", this.scrollHandler) | |
if (this.interval) { | |
clearInterval(this.interval) | |
} | |
} | |
sync() { | |
if (MutationObserver.notSupported) { | |
this.doSync(this.element) | |
} | |
} | |
doSync(element = this.element) { | |
if (element.nodeType !== 1) return | |
const iterable = element.querySelectorAll( | |
`.${this.config.boxClass}` | |
) | |
iterable.forEach((box) => { | |
if (!this.all.includes(box)) { | |
this.boxes.push(box) | |
this.all.push(box) | |
if (this.stopped || this.disabled()) { | |
this.resetStyle() | |
} else { | |
this.applyStyle(box, true) | |
} | |
this.scrolled = true | |
} | |
}) | |
} | |
show(box) { | |
this.applyStyle(box) | |
box.classList.add(this.config.animateClass) | |
if (this.config.callback) this.config.callback(box) | |
emitEvent(box, this.wowEvent) | |
if (this.config.resetAnimation) { | |
;[ | |
"animationend", | |
"oanimationend", | |
"webkitAnimationEnd", | |
"MSAnimationEnd", | |
].forEach((event) => addEvent(box, event, this.resetAnimation)) | |
} | |
return box | |
} | |
applyStyle(box, hidden) { | |
const duration = box.getAttribute("data-wow-duration") | |
const delay = box.getAttribute("data-wow-delay") | |
const iteration = box.getAttribute("data-wow-iteration") | |
this.animate(() => | |
this.customStyle(box, hidden, duration, delay, iteration) | |
) | |
} | |
resetStyle() { | |
this.boxes.forEach((box) => { | |
box.style.visibility = "visible" | |
}) | |
} | |
resetAnimation(event) { | |
if (event.type.toLowerCase().includes("animationend")) { | |
const target = event.target || event.srcElement | |
target.classList.remove(this.config.animateClass) | |
} | |
} | |
customStyle(box, hidden, duration, delay, iteration) { | |
if (hidden) { | |
this.cacheAnimationName(box) | |
} | |
box.style.visibility = hidden ? "hidden" : "visible" | |
if (duration) { | |
box.style.animationDuration = duration | |
} | |
if (delay) { | |
box.style.animationDelay = delay | |
} | |
if (iteration) { | |
box.style.animationIterationCount = iteration | |
} | |
box.style.animationName = hidden | |
? "none" | |
: this.cachedAnimationName(box) | |
return box | |
} | |
vendorSet(elem, properties) { | |
for (const [name, value] of Object.entries(properties)) { | |
elem[name] = value | |
this.vendors.forEach((vendor) => { | |
elem[ | |
`${vendor}${name.charAt(0).toUpperCase()}${name.slice( | |
1 | |
)}` | |
] = value | |
}) | |
} | |
} | |
animationName(box) { | |
try { | |
return getComputedStyle(box).animationName || "none" | |
} catch { | |
return "" | |
} | |
} | |
cacheAnimationName(box) { | |
this.animationNameCache.set(box, this.animationName(box)) | |
} | |
cachedAnimationName(box) { | |
return this.animationNameCache.get(box) | |
} | |
scrollHandler() { | |
this.scrolled = true | |
} | |
scrollCallback() { | |
if (this.scrolled) { | |
this.scrolled = false | |
this.boxes = this.boxes.filter((box) => { | |
if (this.isVisible(box)) { | |
this.show(box) | |
return false | |
} | |
return true | |
}) | |
if (!this.boxes.length && !this.config.live) { | |
this.stop() | |
} | |
} | |
} | |
isVisible(box) { | |
const rect = box.getBoundingClientRect() | |
const windowHeight = getInnerHeight() | |
const top = rect.top | |
return top <= windowHeight && top + rect.height >= 0 | |
} | |
disabled() { | |
return !this.config.mobile && isMobile(navigator.userAgent) | |
} | |
} | |
// Expose WOW class globally | |
exports.WOW = WOW | |
// Auto-initialize WOW when the script is loaded | |
if (typeof window !== "undefined") { | |
window.WOW = WOW | |
} | |
// Initialize wow.js on page load | |
if ( | |
document.readyState === "interactive" || | |
document.readyState === "complete" | |
) { | |
new WOW().init() | |
} else { | |
window.addEventListener("DOMContentLoaded", function () { | |
new WOW().init() | |
}) | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment