Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * A collection of helper prototype for everyday DOM traversal, manipulation,
- * and event binding. Sort of a minimalist jQuery, mainly for demonstration
- * purposes. MIT @ m3g4p0p
- */
- window.$ = (function (undefined) {
- /**
- * Duration constants
- * @type {Object}
- */
- const DURATION = {
- DEFAULT: 500
- }
- /**
- * Style constants
- * @type {Object}
- */
- const STYLE = {
- SHOW: 'block',
- HIDE: 'none'
- }
- /**
- * Style property constants
- * @type {Object}
- */
- const PROPERTY = {
- DISPLAY: 'display'
- }
- /**
- * Type constants
- * @type {Object}
- */
- const TYPE = {
- FUNCTION: 'function',
- STRING: 'string'
- }
- /**
- * Map elements to events
- * @type {WeakMap}
- */
- const eventMap = new WeakMap()
- /**
- * Function to fade an element
- * @param {HTMLElement} element
- * @param {Number} from
- * @param {Number} to
- * @param {Number} duration
- * @param {Function} callback
- * @return {void}
- */
- const fade = function fade (element, from, to, duration, callback) {
- const start = window.performance.now()
- element.style.display = STYLE.SHOW
- window.requestAnimationFrame(function step (timestamp) {
- const progress = timestamp - start
- element.style.opacity = from + (progress / duration) * (to - from)
- if (progress < duration) {
- window.requestAnimationFrame(step)
- } else {
- if (element.style.opacity <= 0) {
- element.style.display = STYLE.HIDE
- }
- if (callback) {
- callback.call(element)
- }
- }
- })
- }
- /**
- * The methods in the collection'S prototype
- * @type {Object}
- */
- const prototype = {
- ///////////////
- // Traversal //
- ///////////////
- /**
- * Check if a node is already contained in the collection
- * @param {HTMLElement} element
- * @return {Boolean}
- */
- has (element) {
- return Array.from(this).includes(element)
- },
- /**
- * Add an element or a list of elements to the collection
- * @param {mixed} element
- * @returns {this}
- */
- add (element) {
- const elements = element.length !== undefined ? element : [element]
- Array.from(elements).forEach(element => {
- if (element && !this.has(element)) {
- Array.prototype.push.call(this, element)
- }
- })
- return this
- },
- /**
- * Find descendants of the current collection matching a selector
- * @param {String} selector
- * @return {this}
- */
- find (selector) {
- return Array.from(this).reduce(
- (carry, element) => carry.add(element.querySelectorAll(selector)),
- Object.create(prototype)
- )
- },
- /**
- * Filter the current collection by a selector or filter function
- * @param {String|Function} selector
- * @return {this}
- */
- filter (selector) {
- return Object.create(prototype).add(
- Array.from(this).filter(
- typeof selector === TYPE.FUNCTION
- ? selector
- : element => element.matches(selector)
- )
- )
- },
- /**
- * Get a collection containing the adjecent next siblings
- * of the current collection, optionally filtered by a selector
- * @param {String|undefined} selector
- * @return {this}
- */
- next (selector) {
- return Object.create(prototype).add(
- Array.from(this)
- .map(element => element.nextElementSibling)
- .filter(element => element && (!selector || element.matches(selector)))
- )
- },
- /**
- * Get a collection containing the adjecent previous siblings
- * of the current collection, optionally filtered by a selector
- * @param {String|undefined} selector
- * @return {this}
- */
- prev (selector) {
- return Object.create(prototype).add(
- Array.from(this)
- .map(element => element.previousElementSibling)
- .filter(element => element && (!selector || element.matches(selector)))
- )
- },
- /**
- * Get a collection containing the immediate parents of
- * the current collection, optionally filtered by a selector
- * @param {String|undefined} selector
- * @return {this}
- */
- parent (selector) {
- return Object.create(prototype).add(
- Array
- .from(this)
- .map(element => element.parentNode)
- .filter(element => !selector || element.matches(selector))
- )
- },
- /**
- * Get a collection containing the immediate parents of the
- * current collection, or, if a selector is specified, the next
- * ancestor that matches that selector
- * @param {String|undefined} selector
- * @return {this}
- */
- parents (selector) {
- return Object.create(prototype).add(
- Array.from(this).map(function walk (element) {
- const parent = element.parentNode
- return parent && (!selector || parent.matches(selector))
- ? parent
- : walk(parent)
- })
- )
- },
- /**
- * Get a collection containing the immediate children of the
- * current collection, optionally filtered by a selector
- * @param {String|undefined} selector
- * @return {this}
- */
- children (selector) {
- return Object.create(prototype).add(
- Array
- .from(this)
- .reduce((carry, element) => carry.concat(...element.children), [])
- .filter(element => !selector || element.matches(selector))
- )
- },
- //////////////////
- // Manipulation //
- //////////////////
- /**
- * Add a class to all elements in the current collection
- * @param {String} className
- * @returns {this}
- */
- addClass (className) {
- Array.from(this).forEach(el => {
- el.classList.add(className)
- })
- return this
- },
- /**
- * Remove a class from all elements in the current collection
- * @param {String} className
- * @return {this}
- */
- removeClass (className) {
- Array.from(this).forEach(el => {
- el.classList.remove(className)
- })
- return this
- },
- /**
- * Set the value property of all elements in the current
- * collection, or, if no value is specified, get the value
- * of the first element in the collection
- * @param {mixed} newVal
- * @return {this}
- */
- val (newVal) {
- if (!newVal) {
- return this[0].value
- }
- Array.from(this).forEach(el => {
- el.value = newVal
- })
- return this
- },
- /**
- * Set the HTML of all elements in the current collection,
- * or, if no markup is specified, get the HTML of the first
- * element in the collection
- * @param {String|undefined} newHtml
- * @return {this}
- */
- html (newHtml) {
- if (!newHtml) {
- return this[0].innerHtml
- }
- Array.from(this).forEach(el => {
- el.innerHtml = newVal
- })
- return this
- },
- /**
- * Set the text of all elements in the current collection,
- * or, if no markup is specified, get the HTML of the first
- * element in the collection
- * @param {String|undefined} newText
- * @return {this}
- */
- text (newText) {
- if (!newText) {
- return this[0].textContent
- }
- Array.from(this).forEach(el => {
- el.textContent = newText
- })
- return this
- },
- ///////////////////////
- // CSS and animation //
- ///////////////////////
- /**
- * Hide all elements in the current collection
- * @return {this}
- */
- hide () {
- Array.from(this).forEach(element => {
- element.style.display = null
- if (window.getComputedStyle(element).getPropertyValue(PROPERTY.DISPLAY) !== STYLE.HIDE) {
- element.style.display = STYLE.HIDE
- }
- })
- return this
- },
- /**
- * Show all elements in the current collection
- * @return {this}
- */
- show () {
- Array.from(this).forEach(element => {
- element.style.display = null
- if (window.getComputedStyle(element).getPropertyValue(PROPERTY.DISPLAY) === STYLE.HIDE) {
- element.style.display = STYLE.SHOW
- }
- })
- return this
- },
- /**
- * Set the CSS of the elements in the current collection
- * by either specifying the CSS property and value, or
- * an object containing the style declarations
- * @param {String|object} style
- * @param {mixed} value
- * @return {this}
- */
- css (style, value) {
- const currentStyle = {}
- if (typeof style === TYPE.STRING) {
- if (!value) {
- return this[0] && window
- .getComputedStyle(this[0])
- .getPropertyValue(style)
- }
- currentStyle[style] = value
- } else {
- Object.assign(currentStyle, style)
- }
- Array.from(this).forEach(element => {
- Object.assign(element.style, currentStyle)
- })
- return this
- },
- /**
- * Fade the elements in the current collection in; optionally
- * takes the fade duration and a callback that gets executed
- * on each element after the animation finished
- * @param {Number|undefined} duration
- * @param {Function|undefined} callback
- * @return {this}
- */
- fadeIn (duration, callback) {
- Array.from(this).forEach(element => {
- fade(element, 0, 1, duration || DURATION.DEFAULT, callback)
- })
- return this
- },
- /**
- * Fade the elements in the current collection out; optionally
- * takes the fade duration and a callback that gets executed
- * on each element after the animation finished
- * @param {Number|undefined} duration
- * @param {Function|undefined} callback
- * @return {this}
- */
- fadeOut (duration, callback) {
- Array.from(this).forEach(element => {
- fade(element, 1, 0, duration || DURATION.DEFAULT, callback)
- })
- return this
- },
- ////////////
- // Events //
- ////////////
- /**
- * Bind event listeners to all elements in the current collection,
- * optionally delegated to a target element specified as 2nd argument
- * @param {String} type
- * @param {Function|String} target
- * @param {Function|undefined} callback
- * @return {this}
- */
- on (type, target, callback) {
- const handler = callback
- ? function (event) {
- if (event.target.matches(target)) {
- callback.call(this, event)
- }
- }
- : target
- Array.from(this).forEach(element => {
- const events = eventMap.get(element) || eventMap.set(element, {}).get(element)
- events[type] = events[type] || []
- events[type].push(handler)
- element.addEventListener(type, handler)
- })
- return this
- },
- /**
- * Remove event listeners from the elements in the current
- * collection; if no handler is specified, all listeners of
- * the given type will be removed
- * @param {String} type
- * @param {Function|undefined} callback
- * @return {this}
- */
- off (type, callback) {
- Array.from(this).forEach(element => {
- const events = eventMap.get(element)
- const callbacks = events && events[type]
- if (callback) {
- element.removeEventListener(type, callback)
- if (callbacks) {
- events[type] = callbacks.filter(current => current !== callback)
- }
- } else if (callbacks) {
- delete events[type]
- callbacks.forEach(callback => {
- element.removeEventListener(type, callback)
- })
- }
- })
- return this
- },
- ///////////////////
- // Miscellaneous //
- ///////////////////
- /**
- * Execute a funtion on each element in the current collection
- * @param {Function} fn
- * @return {this}
- */
- each (fn) {
- Array.from(this).forEach(element => {
- fn.call(element)
- })
- return this
- }
- }
- /**
- * Create a new collection
- * @param {String} selector
- * @param {HTMLElement|undefined} context
- * @return {Object}
- */
- return function createCollection (selector, context) {
- const initial = typeof selector === TYPE.STRING
- ? (context || document).querySelectorAll(selector)
- : selector
- const instance = Object.create(prototype)
- return initial
- ? instance.add(initial)
- : instance
- }
- })()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement