├── splitAnchor.js ├── scrollToTop.js ├── getComputedStyle.js ├── .DS_Store ├── getPageName.js ├── toggleElem.js ├── README.md ├── add-array-values.js ├── forEachLoop.js ├── get-widths-from-list.js ├── objectPropLoop.js ├── transforms.js ├── parallaxOpacity.js ├── removeHyphens.js ├── uppercaseFirstLetter.js ├── removeElem.js ├── getCurrentScroll.js ├── mediaQueryTest.js ├── replaceNoJS.js ├── hasClass.js ├── toggleNav.js ├── addEventListenerFallback.js ├── get-anchor.js ├── getClosestAlt.js ├── stringToHTML.js ├── getViewportSize.js ├── removeQuotes.js ├── supportsSvg.js ├── DOMready.js ├── matchMediaListener.js ├── cssSupports.js ├── delegate-event.js ├── removeClass.js ├── supports.js ├── resetActiveClass.js ├── pointerEvents.js ├── throttle.js ├── checkUrlHash.js ├── eventBinding.js ├── getSvgSprite.js ├── getAbsoluteUrl.js ├── iife.js ├── bind.js ├── remove-array-duplicates.js ├── titleCase.js ├── classList.js ├── capitalizeEachWord.js ├── lazyLoadImages.js ├── loadSVG.js ├── getIndex.js ├── once.js ├── prefixedEvent.js ├── addEvent-loop.js ├── onetime.js ├── getHeight.js ├── matchesSelector.js ├── nodelist.forEach.js ├── add-remove-class.js ├── requestAnimThrottle.js ├── getDocumentHeight.js ├── animationEventListener.js ├── getSupportedPropertyName.js ├── getNextSibling.js ├── stickyElement.js ├── getPreviousSibling.js ├── isInViewport.js ├── manipulateDom.js ├── traverse.js ├── getNextUntil.js ├── getPreviousUntil.js ├── isValidEmail.js ├── getSiblings.js ├── insertRule.js ├── ready.js ├── svg.js ├── optimizedScrollEvent.js ├── checkMedia.js ├── getElemDistance.js ├── getMaxWidth.js ├── handleEvent.js ├── loadScript.js ├── getElemInfo.js ├── closestParent.js ├── on.js ├── truncate.js ├── whichTransitionEvent.js ├── loops.js ├── bindPolyfill.js ├── getQueryString.js ├── skip-link-focus-fix.js ├── debounce.js ├── reverseHeader.js ├── revealing-module-pattern.js ├── nextUntil.js ├── prevUntil.js ├── animate.js ├── getJSONP.js ├── autoHideSticky.js ├── poll.js ├── checkActiveCategory.js ├── cross-bowser-class-handlers.js ├── checkVisibility.js ├── getClosest.js ├── parallaxPosition.js ├── cutsMustard.js ├── show-nav-desc.js ├── getParents.js ├── iconsFallback.js ├── forEach.js ├── getParentsUntil.js ├── extend.js ├── onAnimationEnd.js ├── getURL.js ├── fluidVids.js ├── revealing-module-pattern-options.js ├── loadCSS.js ├── navigation.js ├── revealing-module-pattern-constructor.js ├── event-polyfill.js ├── addEventListenerPolyfill.js ├── isVisible.js ├── stickyFooter.js └── classList-polyfill.js /splitAnchor.js: -------------------------------------------------------------------------------- 1 | anchor.href.split('#')[1] -------------------------------------------------------------------------------- /scrollToTop.js: -------------------------------------------------------------------------------- 1 | // Scroll window to top 2 | window.scrollTop = 0; -------------------------------------------------------------------------------- /getComputedStyle.js: -------------------------------------------------------------------------------- 1 | window.getComputedStyle(header,null).getPropertyValue('top') -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonyablonski/javascript-toolbox/HEAD/.DS_Store -------------------------------------------------------------------------------- /getPageName.js: -------------------------------------------------------------------------------- 1 | var pageName = window.location.pathname.substring(1).split(/[/.]+/g)[0]; -------------------------------------------------------------------------------- /toggleElem.js: -------------------------------------------------------------------------------- 1 | var toggleElem = function( elem ) { 2 | elem.classList.toggle( activeClass ); 3 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | javascript-toolbox 2 | ================== 3 | 4 | A collection of useful javascript snippets. 5 | -------------------------------------------------------------------------------- /add-array-values.js: -------------------------------------------------------------------------------- 1 | let sum = item.reduce((accumulator, value) => { 2 | return accumulator + value; 3 | }, 0); -------------------------------------------------------------------------------- /forEachLoop.js: -------------------------------------------------------------------------------- 1 | [].forEach.call(element, function(el) { 2 | el.addEventListener('click', fnName, false); 3 | }); -------------------------------------------------------------------------------- /get-widths-from-list.js: -------------------------------------------------------------------------------- 1 | let itemWidths = Array.from(item).map(function (item) { 2 | return item.offsetWidth; 3 | }); -------------------------------------------------------------------------------- /objectPropLoop.js: -------------------------------------------------------------------------------- 1 | Object.keys(coordinates).forEach(function(key) { 2 | console.log(key, coordinates[key]); 3 | }); -------------------------------------------------------------------------------- /transforms.js: -------------------------------------------------------------------------------- 1 | var transforms = [ 2 | "transform", 3 | "msTransform", 4 | "webkitTransform", 5 | "mozTransform", 6 | "oTransform"]; -------------------------------------------------------------------------------- /parallaxOpacity.js: -------------------------------------------------------------------------------- 1 | var parallaxOpacity = function(element) { 2 | element.style.opacity = 1-Math.floor(latestKnownScrollY)/1000; 3 | }; -------------------------------------------------------------------------------- /removeHyphens.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove Hyphens 3 | */ 4 | var removeHyphens = function(string) { 5 | return string.replace(/-/g, ' '); 6 | }; -------------------------------------------------------------------------------- /uppercaseFirstLetter.js: -------------------------------------------------------------------------------- 1 | var uppercaseFirstLetter = function( string ) { 2 | return string.charAt(0).toUpperCase() + string.slice(1); 3 | }; -------------------------------------------------------------------------------- /removeElem.js: -------------------------------------------------------------------------------- 1 | var removeElement = function( element ) { 2 | if ( element.parentNode ) { 3 | element.parentNode.removeChild( element ); 4 | } 5 | }; -------------------------------------------------------------------------------- /getCurrentScroll.js: -------------------------------------------------------------------------------- 1 | var getCurrentScroll = function() { 2 | return { 3 | x: window.pageXOffset, 4 | y: window.pageYOffset 5 | }; 6 | }; -------------------------------------------------------------------------------- /mediaQueryTest.js: -------------------------------------------------------------------------------- 1 | // Test for CSS media query support 2 | if ( window.matchMedia("only all").matches ) { 3 | // Media Queries are natively supported 4 | } -------------------------------------------------------------------------------- /replaceNoJS.js: -------------------------------------------------------------------------------- 1 | // Replace 'no-js' w/ 'js' on document el 2 | document.documentElement.className = document.documentElement.className.replace('no-js', 'js' ); -------------------------------------------------------------------------------- /hasClass.js: -------------------------------------------------------------------------------- 1 | // hasClass, takes two params: element and classname 2 | function hasClass(el, cls) { 3 | return el.className && new RegExp("(\\s|^)" + cls + "(\\s|$)").test(el.className); 4 | } -------------------------------------------------------------------------------- /toggleNav.js: -------------------------------------------------------------------------------- 1 | var toggleNav = function(e) { 2 | body.classList.contains(toggledNavClass) ? body.classList.remove(toggledNavClass) : body.classList.add(toggledNavClass); 3 | e.preventDefault(); 4 | }; -------------------------------------------------------------------------------- /addEventListenerFallback.js: -------------------------------------------------------------------------------- 1 | if (document.addEventListener) { 2 | el.addEventListener('click', modifyText, false); 3 | } else if (document.attachEvent) { 4 | el.attachEvent('onclick', modifyText); 5 | } -------------------------------------------------------------------------------- /get-anchor.js: -------------------------------------------------------------------------------- 1 | var anchor = event.target; 2 | 3 | // Go up in the nodelist until we find a node with .href (HTMLAnchorElement) 4 | while ( anchor && !anchor.href ) { 5 | anchor = anchor.parentNode; 6 | } -------------------------------------------------------------------------------- /getClosestAlt.js: -------------------------------------------------------------------------------- 1 | const getClosest = function (elem, selector) { 2 | for (; elem && elem !== document; elem = elem.parentNode) { 3 | if (elem.matches(selector)) return elem; 4 | } 5 | return null; 6 | }; -------------------------------------------------------------------------------- /stringToHTML.js: -------------------------------------------------------------------------------- 1 | // Turn strings into HTML 2 | const stringToHTML = (str) => { 3 | let parser = new DOMParser(); 4 | let doc = parser.parseFromString(str, 'text/html'); 5 | return doc.body.firstChild; 6 | } -------------------------------------------------------------------------------- /getViewportSize.js: -------------------------------------------------------------------------------- 1 | var getViewportSize = function() { 2 | return { 3 | width: window.document.documentElement.clientWidth, 4 | height: window.document.documentElement.clientHeight 5 | }; 6 | }; -------------------------------------------------------------------------------- /removeQuotes.js: -------------------------------------------------------------------------------- 1 | function removeQuotes(string) { 2 | if (typeof string === 'string' || string instanceof String) { 3 | string = string.replace(/^['"]+|\s+|\\|(;\s?})+|['"]$/g, ''); 4 | } 5 | return string; 6 | } -------------------------------------------------------------------------------- /supportsSvg.js: -------------------------------------------------------------------------------- 1 | var supportsSvg = function() { 2 | var div = document.createElement('div'); 3 | div.innerHTML = ''; 4 | return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg'; 5 | }; -------------------------------------------------------------------------------- /DOMready.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Document Ready 3 | * Same as jQuery .ready method 4 | * ! Putting JS at the bottom of the page does the same thing 5 | */ 6 | document.addEventListener("DOMContentLoaded", function() { 7 | // Code 8 | }, false); -------------------------------------------------------------------------------- /matchMediaListener.js: -------------------------------------------------------------------------------- 1 | window.matchMedia( "(min-width: 45em)" ).addListener( function( mm ) { 2 | if ( mm.matches ) { 3 | // The viewport is at least 45em in width 4 | } else { 5 | // The viewport is less than 45em in width 6 | } 7 | }); -------------------------------------------------------------------------------- /cssSupports.js: -------------------------------------------------------------------------------- 1 | // CSS Supports normalization 2 | var cssSupports = window.CSS && window.CSS.supports || window.supportsCSS; 3 | 4 | // Usage 5 | if ( cssSupports && cssSupports ('(transition: none)') ) { 6 | // CSS transitions are supported! 7 | } -------------------------------------------------------------------------------- /delegate-event.js: -------------------------------------------------------------------------------- 1 | var functionName = function () { 2 | element.addEventListener( 'click', function( event ) { 3 | if( event.target && event.target.nodeName === 'A' ) { 4 | // Do Something 5 | } 6 | }); 7 | }; 8 | 9 | functionName(); -------------------------------------------------------------------------------- /removeClass.js: -------------------------------------------------------------------------------- 1 | // removeClass, takes two params: element and classname 2 | function removeClass(el, cls) { 3 | var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)"); 4 | el.className = el.className.replace(reg, " ").replace(/(^\s*)|(\s*$)/g,""); 5 | } -------------------------------------------------------------------------------- /supports.js: -------------------------------------------------------------------------------- 1 | var supports = !!document.querySelector && !!window.addEventListener; 2 | 3 | var cutMustard = (function(){ 4 | if ( !supports ) { 5 | document.documentElement.className += ' ' + 'cuts-mustard'; 6 | } else { 7 | return; 8 | } 9 | })(); -------------------------------------------------------------------------------- /resetActiveClass.js: -------------------------------------------------------------------------------- 1 | var resetActiveClass = function ( elem ) { 2 | var siblings = getSiblings( elem ); 3 | forEach( siblings, function ( value ) { 4 | if (value.classList.contains( activeClass )) { 5 | value.classList.remove( activeClass ); 6 | } 7 | }); 8 | }; -------------------------------------------------------------------------------- /pointerEvents.js: -------------------------------------------------------------------------------- 1 | function pointerEvents() { 2 | var element = document.createElement('x'); 3 | element.style.cssText = 'pointer-events:auto'; 4 | return element.style.pointerEvents === 'auto'; 5 | } 6 | if (pointerEvents()) { 7 | document.documentElement.className += ' pointer-events'; 8 | } -------------------------------------------------------------------------------- /throttle.js: -------------------------------------------------------------------------------- 1 | var throttle = function(callback, limit) { 2 | var wait = false; 3 | return function () { 4 | if (!wait) { 5 | callback.call(); 6 | wait = true; 7 | setTimeout(function () { 8 | wait = false; 9 | }, limit); 10 | } 11 | }; 12 | }; -------------------------------------------------------------------------------- /checkUrlHash.js: -------------------------------------------------------------------------------- 1 | var checkURL = (function() { 2 | if ( window.location.hash ) { 3 | var hash = window.location.hash.substring(1); 4 | if ( hash === 'newsletter' ) { 5 | var trigger = document.querySelector( '.intro__news' ).querySelector( 'a' ); 6 | trigger.click(); 7 | } 8 | } 9 | })(); -------------------------------------------------------------------------------- /eventBinding.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This gives us simple dollar function and event binding 3 | */ 4 | var $ = document.querySelectorAll.bind(document); 5 | Element.prototype.on = Element.prototype.addEventListener; 6 | 7 | // This is how you use it 8 | $(".element")[0].on("touchstart", handleTouch, false); -------------------------------------------------------------------------------- /getSvgSprite.js: -------------------------------------------------------------------------------- 1 | var ajax = new XMLHttpRequest(); 2 | ajax.open("GET", "svg/sprite.svg", true); 3 | ajax.send(); 4 | ajax.onload = function(e) { 5 | var div = document.createElement("div"); 6 | div.innerHTML = ajax.responseText; 7 | document.body.insertBefore(div, document.body.childNodes[0]); 8 | } -------------------------------------------------------------------------------- /getAbsoluteUrl.js: -------------------------------------------------------------------------------- 1 | var getAbsoluteUrl = (function() { 2 | var a; 3 | 4 | return function(url) { 5 | if(!a) a = document.createElement('a'); 6 | a.href = url; 7 | 8 | return a.href; 9 | }; 10 | })(); 11 | 12 | // Usage 13 | getAbsoluteUrl('/something'); // https://davidwalsh.name/something -------------------------------------------------------------------------------- /iife.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Immediately Invoked Function Expression Boilerplate 3 | * (c) 2017 Chris Ferdinandi, MIT License, https://gomakethings.com 4 | */ 5 | ;(function (window, document, undefined) { 6 | 7 | 'use strict'; 8 | 9 | // Code goes here... 10 | 11 | })(window, document); 12 | -------------------------------------------------------------------------------- /bind.js: -------------------------------------------------------------------------------- 1 | var elem = document.querySelector('.some-class'); 2 | var someFunction = function (var1, var2, var3, event) { 3 | // Do stuff 4 | } 5 | elem.addEventListener('click', someFunction.bind(null, var1, var2, var3), false); 6 | elem.addEventListener('mouseover', someFunction.bind(null, var1, var2, var3), false); -------------------------------------------------------------------------------- /remove-array-duplicates.js: -------------------------------------------------------------------------------- 1 | var sandwiches = ['turkey', 'ham', 'turkey', 'tuna', 'pb&j', 'ham', 'turkey', 'tuna']; 2 | 3 | var deduped = sandwiches.filter(function (sandwich, index) { 4 | return sandwiches.indexOf(sandwich) === index; 5 | }); 6 | 7 | // Logs ["turkey", "ham", "tuna", "pb&j"] 8 | console.log(deduped); -------------------------------------------------------------------------------- /titleCase.js: -------------------------------------------------------------------------------- 1 | var titleCase = function(str) { 2 | var splitStr = str.toLowerCase().split(' '); 3 | for (var i = 0; i < splitStr.length; i++) { 4 | splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1); 5 | } 6 | return splitStr.join(' '); 7 | }; 8 | 9 | // Usage 10 | titleCase(stringName); -------------------------------------------------------------------------------- /classList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * classList class methods 3 | */ 4 | 5 | // Adding a class 6 | element.classList.add("bar"); 7 | 8 | // Removing a class 9 | element.classList.remove("foo"); 10 | 11 | // Checking if has a class 12 | element.classList.contains("foo"); 13 | 14 | // Toggle a class 15 | element.classList.toggle("active"); -------------------------------------------------------------------------------- /capitalizeEachWord.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Capitalize Each Word 3 | */ 4 | var capitalizeEachWord = function(string) { 5 | var splitStr = string.toLowerCase().split(' '); 6 | for (var i = 0; i < splitStr.length; i++) { 7 | splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1); 8 | } 9 | return splitStr.join(' '); 10 | }; -------------------------------------------------------------------------------- /lazyLoadImages.js: -------------------------------------------------------------------------------- 1 | [].forEach.call(document.querySelectorAll('noscript'), function(noscript) { 2 | var img = new Image(); 3 | img.setAttribute('data-src', ''); 4 | img.parentNode.insertBefore(img, noscript); 5 | img.onload = function() { 6 | img.removeAttribute('data-src'); 7 | }; 8 | img.src = noscript.getAttribute('data-src'); 9 | }); -------------------------------------------------------------------------------- /loadSVG.js: -------------------------------------------------------------------------------- 1 | var loadSVG = function() { 2 | var ajax = new XMLHttpRequest(); 3 | ajax.open('GET', svgSymbols, true); 4 | ajax.send(); 5 | ajax.onload = function() { 6 | var div = document.createElement('div'); 7 | div.style.display = 'none'; 8 | div.innerHTML = ajax.responseText; 9 | document.body.insertBefore(div, document.body.childNodes[0]); 10 | }; 11 | }; -------------------------------------------------------------------------------- /getIndex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {Element} el 3 | * @param {string} [selector] 4 | * @return {number} 5 | */ 6 | 7 | getIndex = function(el, selector) { 8 | var i = 0; 9 | 10 | while ((el = el.previousElementSibling) !== null) { 11 | if (!selector || el.matches(selector)) { 12 | ++i; 13 | } 14 | } 15 | 16 | return i; 17 | }; -------------------------------------------------------------------------------- /once.js: -------------------------------------------------------------------------------- 1 | function once(fn, context) { 2 | var result; 3 | 4 | return function() { 5 | if(fn) { 6 | result = fn.apply(context || this, arguments); 7 | fn = null; 8 | } 9 | 10 | return result; 11 | }; 12 | } 13 | 14 | // Usage 15 | var canOnlyFireOnce = once(function() { 16 | console.log('Fired!'); 17 | }); 18 | 19 | canOnlyFireOnce(); // "Fired!" 20 | canOnlyFireOnce(); // nada -------------------------------------------------------------------------------- /prefixedEvent.js: -------------------------------------------------------------------------------- 1 | var pfx = ["webkit", "moz", "MS", "o", ""]; 2 | 3 | var prefixedEventListener = function( element, type, callback ) { 4 | for ( var p = 0; p < pfx.length; p++ ) { 5 | if ( !pfx[p] ) { 6 | type = type.toLowerCase(); 7 | } 8 | element.addEventListener( pfx[p]+type, callback, false ); 9 | } 10 | }; 11 | 12 | // Usage 13 | prefixedEvent(entry, "AnimationEnd", deleteElem); -------------------------------------------------------------------------------- /addEvent-loop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * addEventListener to collection 3 | */ 4 | 5 | // Select all links 6 | var links = document.querySelectorAll("a"); 7 | 8 | // For each link element 9 | [].forEach.call(links, function(el) { 10 | 11 | // Add event listener 12 | el.addEventListener("click", function(event) { 13 | event.preventDefault(); 14 | alert("You clicked"); 15 | }, false); 16 | 17 | }); -------------------------------------------------------------------------------- /onetime.js: -------------------------------------------------------------------------------- 1 | // create a one-time event 2 | var onetime = function (node, type, callback) { 3 | 4 | // create event 5 | node.addEventListener(type, function(e) { 6 | // remove event 7 | e.target.removeEventListener(e.type, arguments.callee); 8 | // call handler 9 | return callback(e); 10 | }); 11 | 12 | }; 13 | 14 | //onetime(document.getElementById("myelement"), "click", handler); -------------------------------------------------------------------------------- /getHeight.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the height of an element 3 | * @param {Node} elem The element 4 | * @return {Number} The height 5 | */ 6 | var getHeight = function ( elem ) { 7 | return Math.max( elem.scrollHeight, elem.offsetHeight, elem.clientHeight ); 8 | }; 9 | 10 | var elem = document.querySelector( '#some-element' ); 11 | var height = getHeight( elem ); // Get height 12 | elem.style.height = '200px'; // Set height -------------------------------------------------------------------------------- /matchesSelector.js: -------------------------------------------------------------------------------- 1 | function matchesSelector(el, selector) { 2 | var p = Element.prototype; 3 | var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) { 4 | return [].indexOf.call(document.querySelectorAll(s), this) !== -1; 5 | }; 6 | return f.call(el, selector); 7 | } 8 | 9 | // Usage 10 | matchesSelector(document.getElementById('myDiv'), 'div.someSelector[some-attribute=true]') -------------------------------------------------------------------------------- /nodelist.forEach.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NodeList.prototype.forEach() polyfill 3 | * https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill 4 | */ 5 | if (window.NodeList && !NodeList.prototype.forEach) { 6 | NodeList.prototype.forEach = function (callback, thisArg) { 7 | thisArg = thisArg || window; 8 | for (var i = 0; i < this.length; i++) { 9 | callback.call(thisArg, this[i], i, this); 10 | } 11 | }; 12 | } -------------------------------------------------------------------------------- /add-remove-class.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Various ways to add/remove classes 3 | */ 4 | 5 | // Select an element 6 | var element = document.querySelector(".some-class"); 7 | 8 | // Give class "foo" to the element 9 | element.className = "foo"; 10 | 11 | // Adding a class without replacing the current classes 12 | element.className += " foo"; 13 | 14 | // Removing "no-js" class from html-element and replacing it with "js" 15 | document.documentElement.className = "js"; -------------------------------------------------------------------------------- /requestAnimThrottle.js: -------------------------------------------------------------------------------- 1 | function throttle(action) { 2 | let isRunning = false; 3 | return function() { 4 | if (isRunning) return; 5 | isRunning = true; 6 | window.requestAnimationFrame(() => { 7 | action(); 8 | isRunning = false; 9 | }); 10 | } 11 | } 12 | 13 | //Usage 14 | 15 | window.addEventListener('scroll', throttle(() => { 16 | const scrollTop = window.scrollY; 17 | /* doSomething with scrollTop */ 18 | })); 19 | 20 | -------------------------------------------------------------------------------- /getDocumentHeight.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the height of the `document` element 3 | * @return {Number} The height 4 | */ 5 | var getDocumentHeight = function () { 6 | return Math.max( 7 | document.body.scrollHeight, 8 | document.documentElement.scrollHeight, 9 | document.body.offsetHeight, 10 | document.documentElement.offsetHeight, 11 | document.body.clientHeight, 12 | document.documentElement.clientHeight 13 | ); 14 | }; -------------------------------------------------------------------------------- /animationEventListener.js: -------------------------------------------------------------------------------- 1 | var pfx = ["webkit", "moz", "MS", "o", ""]; 2 | function prefixedEventListener(element, type, callback) { 3 | for (var p = 0; p < pfx.length; p++) { 4 | if (!pfx[p]) type = type.toLowerCase(); 5 | element.addEventListener(pfx[p]+type, callback, false); 6 | } 7 | } 8 | 9 | // var monkey = document.querySelector("#monkey"); 10 | // prefixedEventListener(monkey,"AnimationStart",function(e){ 11 | // console.log("log at beginning of monkey animation"); 12 | // }); -------------------------------------------------------------------------------- /getSupportedPropertyName.js: -------------------------------------------------------------------------------- 1 | var getSupportedPropertyName = function(properties) { 2 | for (var i = 0; i < properties.length; i++) { 3 | if (typeof document.body.style[properties[i]] !== 'undefined') { 4 | return properties[i]; 5 | } 6 | } 7 | return null; 8 | }; 9 | 10 | var transform = ['transform', 'msTransform', 'webkitTransform', 'mozTransform', 'oTransform']; 11 | 12 | // Usage 13 | elem.style[getSupportedPropertyName(transform)] = 'translate3d(0px,' + Math.round(wScrollCurrent * rate) + 'px, 0px)'; -------------------------------------------------------------------------------- /getNextSibling.js: -------------------------------------------------------------------------------- 1 | const getNextSibling = function (elem, selector) { 2 | 3 | // Get the next sibling element 4 | let sibling = elem.nextElementSibling; 5 | 6 | // If there's no selector, return the first sibling 7 | if (!selector) return sibling; 8 | 9 | // If the sibling matches our selector, use it 10 | // If not, jump to the next sibling and continue the loop 11 | while (sibling) { 12 | if (sibling.matches(selector)) return sibling; 13 | sibling = sibling.nextElementSibling 14 | } 15 | 16 | }; -------------------------------------------------------------------------------- /stickyElement.js: -------------------------------------------------------------------------------- 1 | var stickyElement = function(element, offsetElement, spacing) { 2 | var distanceFromTop = getElemDistance(element), 3 | offsetDistance = offsetElement.clientHeight + spacing; 4 | if ( latestKnownScrollY > (distanceFromTop - offsetDistance) ) { 5 | element.classList.add('is-sticky'); 6 | } else if (latestKnownScrollY < (distanceFromTop - offsetDistance) && element.classList.contains('is-sticky') ) { 7 | element.classList.remove('is-sticky'); 8 | } else { 9 | return; 10 | } 11 | }; -------------------------------------------------------------------------------- /getPreviousSibling.js: -------------------------------------------------------------------------------- 1 | const getPreviousSibling = function (elem, selector) { 2 | 3 | // Get the next sibling element 4 | let sibling = elem.previousElementSibling; 5 | 6 | // If there's no selector, return the first sibling 7 | if (!selector) return sibling; 8 | 9 | // If the sibling matches our selector, use it 10 | // If not, jump to the next sibling and continue the loop 11 | while (sibling) { 12 | if (sibling.matches(selector)) return sibling; 13 | sibling = sibling.previousElementSibling; 14 | } 15 | 16 | }; -------------------------------------------------------------------------------- /isInViewport.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Determine if an element is in the visible viewport 3 | */ 4 | var isInViewport = function ( elem ) { 5 | var distance = elem.getBoundingClientRect(); 6 | return ( 7 | distance.top >= 0 && 8 | distance.left >= 0 && 9 | distance.bottom <= (window.innerHeight || document.documentElement.clientHeight) && 10 | distance.right <= (window.innerWidth || document.documentElement.clientWidth) 11 | ); 12 | }; 13 | var elem = document.querySelector('#some-element'); 14 | isInViewport(elem); // Boolean: returns true/false -------------------------------------------------------------------------------- /manipulateDom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Manipulate DOM 3 | */ 4 | 5 | // Select an element 6 | var element = document.querySelector(".class"); 7 | 8 | // Clone it 9 | var clone = element.cloneNode(true); 10 | 11 | // Do some manipulation off the DOM 12 | clone.style.background = "#000"; 13 | 14 | // Replaces the original element with the new cloned one 15 | element.parentNode.replaceChild(clone, element); 16 | 17 | // Don't replace anything in the DOM 18 | // Instead append the newly created div inside the 19 | document.body.appendChild(clone); -------------------------------------------------------------------------------- /traverse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Traversing the DOM 3 | */ 4 | 5 | // Getting the parent node 6 | var parent = document.querySelector("div").parentNode; 7 | 8 | // Getting the next node 9 | var next = document.querySelector("div").nextSibling; 10 | 11 | // Getting the previous node 12 | var next = document.querySelector("div").previousSibling; 13 | 14 | // Getting the first child element 15 | var child = document.querySelector("div").children[0]; 16 | 17 | // Getting the last child 18 | var last = document.querySelector("div").lastElementChild; 19 | -------------------------------------------------------------------------------- /getNextUntil.js: -------------------------------------------------------------------------------- 1 | const getNextUntil = function (elem, selector) { 2 | 3 | // Setup siblings array and get next sibling 4 | var siblings = []; 5 | var next = elem.nextElementSibling; 6 | 7 | // Loop through all siblings 8 | while (next) { 9 | 10 | // If the matching item is found, quit 11 | if (selector && next.matches(selector)) break; 12 | 13 | // Otherwise, push to array 14 | siblings.push(next); 15 | 16 | // Get the next sibling 17 | next = next.nextElementSibling 18 | 19 | } 20 | 21 | return siblings; 22 | 23 | }; -------------------------------------------------------------------------------- /getPreviousUntil.js: -------------------------------------------------------------------------------- 1 | var getPreviousUntil = function (elem, selector) { 2 | 3 | // Setup siblings array and get previous sibling 4 | var siblings = []; 5 | var prev = elem.previousElementSibling; 6 | 7 | // Loop through all siblings 8 | while (prev) { 9 | 10 | // If the matching item is found, quit 11 | if (selector && prev.matches(selector)) break; 12 | 13 | // Otherwise, push to array 14 | siblings.push(prev); 15 | 16 | // Get the previous sibling 17 | prev = prev.previousElementSibling 18 | 19 | } 20 | 21 | return siblings; 22 | 23 | }; -------------------------------------------------------------------------------- /isValidEmail.js: -------------------------------------------------------------------------------- 1 | function isValidEmail(string) { 2 | string = string||''; 3 | var lastseg = (string.split('@')[1]||'').split('.')[1]||'', 4 | input = document.createElement('input'); 5 | input.type = 'email'; 6 | 7 | input.required = true; 8 | input.value = string; 9 | return !!(string && (input.validity && input.validity.valid) && lastseg.length); 10 | } 11 | 12 | console.log(isValidEmail('')); // -> false 13 | console.log(isValidEmail('asda')); // -> false 14 | console.log(isValidEmail('asda@gmail')); // -> false 15 | console.log(isValidEmail('asda@gmail.com')); // -> true -------------------------------------------------------------------------------- /getSiblings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get all siblings of an element 3 | * @param {Node} elem The element 4 | * @return {Array} The siblings 5 | */ 6 | var getSiblings = function ( elem ) { 7 | var siblings = []; 8 | var sibling = elem.parentNode.firstChild; 9 | for ( ; sibling; sibling = sibling.nextSibling ) { 10 | if ( sibling.nodeType === 1 && sibling !== elem ) { 11 | siblings.push( sibling ); 12 | } 13 | } 14 | return siblings; 15 | }; 16 | 17 | var elem = document.querySelector( '#some-element' ); 18 | var siblings = getSiblings( elem ); -------------------------------------------------------------------------------- /insertRule.js: -------------------------------------------------------------------------------- 1 | var sheet = (function() { 2 | // Create the '; 52 | head.appendChild(div.childNodes[1]); 53 | }; 54 | 55 | fluidvids.render = function () { 56 | var nodes = document.querySelectorAll(fluidvids.selector.join()); 57 | var i = nodes.length; 58 | while (i--) { 59 | fluid(nodes[i]); 60 | } 61 | }; 62 | 63 | fluidvids.init = function (obj) { 64 | for (var key in obj) { 65 | fluidvids[key] = obj[key]; 66 | } 67 | fluidvids.render(); 68 | addStyles(); 69 | }; 70 | 71 | return fluidvids; 72 | 73 | }); -------------------------------------------------------------------------------- /revealing-module-pattern-options.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Revealing Module Pattern with User Options Boilerplate 3 | * (c) 2017 Chris Ferdinandi, MIT License, https://gomakethings.com 4 | */ 5 | var myPlugin = (function () { 6 | 7 | 'use strict'; 8 | 9 | // 10 | // Variables 11 | // 12 | 13 | var publicAPIs = {}; 14 | var defaults = {}; 15 | var settings; 16 | 17 | 18 | // 19 | // Methods 20 | // 21 | 22 | /*! 23 | * Merge two or more objects together. 24 | * (c) 2017 Chris Ferdinandi, MIT License, https://gomakethings.com 25 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional] 26 | * @param {Object} objects The objects to merge together 27 | * @returns {Object} Merged values of defaults and options 28 | */ 29 | var extend = function () { 30 | 31 | // Variables 32 | var extended = {}; 33 | var deep = false; 34 | var i = 0; 35 | 36 | // Check if a deep merge 37 | if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) { 38 | deep = arguments[0]; 39 | i++; 40 | } 41 | 42 | // Merge the object into the extended object 43 | var merge = function (obj) { 44 | for (var prop in obj) { 45 | if (obj.hasOwnProperty(prop)) { 46 | // If property is an object, merge properties 47 | if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') { 48 | extended[prop] = extend(extended[prop], obj[prop]); 49 | } else { 50 | extended[prop] = obj[prop]; 51 | } 52 | } 53 | } 54 | }; 55 | 56 | // Loop through each object and conduct a merge 57 | for (; i < arguments.length; i++) { 58 | var obj = arguments[i]; 59 | merge(obj); 60 | } 61 | 62 | return extended; 63 | 64 | }; 65 | 66 | /** 67 | * A private method 68 | */ 69 | var somePrivateMethod = function () { 70 | // Code goes here... 71 | }; 72 | 73 | /** 74 | * A public method 75 | */ 76 | publicAPIs.doSomething = function () { 77 | somePrivateMethod(); 78 | // Code goes here... 79 | }; 80 | 81 | /** 82 | * Another public method 83 | */ 84 | publicAPIs.init = function (options) { 85 | 86 | // Merge options into defaults 87 | settings = extend(defaults, options || {}); 88 | 89 | // Code goes here... 90 | 91 | }; 92 | 93 | 94 | // 95 | // Return the Public APIs 96 | // 97 | 98 | return publicAPIs; 99 | 100 | })(); 101 | 102 | myPlugin.init({ 103 | mayo: true, 104 | bread: 'rye', 105 | }); -------------------------------------------------------------------------------- /loadCSS.js: -------------------------------------------------------------------------------- 1 | /*! 2 | loadCSS: load a CSS file asynchronously. 3 | [c]2015 @scottjehl, Filament Group, Inc. 4 | Licensed MIT 5 | */ 6 | (function(w){ 7 | "use strict"; 8 | /* exported loadCSS */ 9 | var loadCSS = function( href, before, media ){ 10 | // Arguments explained: 11 | // `href` [REQUIRED] is the URL for your CSS file. 12 | // `before` [OPTIONAL] is the element the script should use as a reference for injecting our stylesheet before 13 | // By default, loadCSS attempts to inject the link after the last stylesheet or script in the DOM. However, you might desire a more specific location in your document. 14 | // `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all' 15 | var doc = w.document; 16 | var ss = doc.createElement( "link" ); 17 | var ref; 18 | if( before ){ 19 | ref = before; 20 | } 21 | else { 22 | var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes; 23 | ref = refs[ refs.length - 1]; 24 | } 25 | 26 | var sheets = doc.styleSheets; 27 | ss.rel = "stylesheet"; 28 | ss.href = href; 29 | // temporarily set media to something inapplicable to ensure it'll fetch without blocking render 30 | ss.media = "only x"; 31 | 32 | // Inject link 33 | // Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs 34 | // Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/ 35 | ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) ); 36 | // A method (exposed on return object for external use) that mimics onload by polling until document.styleSheets until it includes the new sheet. 37 | var onloadcssdefined = function( cb ){ 38 | var resolvedHref = ss.href; 39 | var i = sheets.length; 40 | while( i-- ){ 41 | if( sheets[ i ].href === resolvedHref ){ 42 | return cb(); 43 | } 44 | } 45 | setTimeout(function() { 46 | onloadcssdefined( cb ); 47 | }); 48 | }; 49 | 50 | // once loaded, set link's media back to `all` so that the stylesheet applies once it loads 51 | ss.onloadcssdefined = onloadcssdefined; 52 | onloadcssdefined(function() { 53 | ss.media = media || "all"; 54 | }); 55 | return ss; 56 | }; 57 | // commonjs 58 | if( typeof module !== "undefined" ){ 59 | module.exports = loadCSS; 60 | } 61 | else { 62 | w.loadCSS = loadCSS; 63 | } 64 | }( typeof global !== "undefined" ? global : this )); -------------------------------------------------------------------------------- /navigation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * navigation.js 3 | * 4 | * Handles toggling the navigation menu for small screens and enables tab 5 | * support for dropdown menus. 6 | */ 7 | ( function() { 8 | var container, button, menu, links, subMenus; 9 | 10 | container = document.getElementById( 'site-navigation' ); 11 | if ( ! container ) { 12 | return; 13 | } 14 | 15 | button = container.getElementsByTagName( 'button' )[0]; 16 | if ( 'undefined' === typeof button ) { 17 | return; 18 | } 19 | 20 | menu = container.getElementsByTagName( 'ul' )[0]; 21 | 22 | // Hide menu toggle button if menu is empty and return early. 23 | if ( 'undefined' === typeof menu ) { 24 | button.style.display = 'none'; 25 | return; 26 | } 27 | 28 | menu.setAttribute( 'aria-expanded', 'false' ); 29 | if ( -1 === menu.className.indexOf( 'nav-menu' ) ) { 30 | menu.className += ' nav-menu'; 31 | } 32 | 33 | button.onclick = function() { 34 | if ( -1 !== container.className.indexOf( 'toggled' ) ) { 35 | container.className = container.className.replace( ' toggled', '' ); 36 | button.setAttribute( 'aria-expanded', 'false' ); 37 | menu.setAttribute( 'aria-expanded', 'false' ); 38 | } else { 39 | container.className += ' toggled'; 40 | button.setAttribute( 'aria-expanded', 'true' ); 41 | menu.setAttribute( 'aria-expanded', 'true' ); 42 | } 43 | }; 44 | 45 | // Get all the link elements within the menu. 46 | links = menu.getElementsByTagName( 'a' ); 47 | subMenus = menu.getElementsByTagName( 'ul' ); 48 | 49 | // Set menu items with submenus to aria-haspopup="true". 50 | for ( var i = 0, len = subMenus.length; i < len; i++ ) { 51 | subMenus[i].parentNode.setAttribute( 'aria-haspopup', 'true' ); 52 | } 53 | 54 | // Each time a menu link is focused or blurred, toggle focus. 55 | for ( i = 0, len = links.length; i < len; i++ ) { 56 | links[i].addEventListener( 'focus', toggleFocus, true ); 57 | links[i].addEventListener( 'blur', toggleFocus, true ); 58 | } 59 | 60 | /** 61 | * Sets or removes .focus class on an element. 62 | */ 63 | function toggleFocus() { 64 | var self = this; 65 | 66 | // Move up through the ancestors of the current link until we hit .nav-menu. 67 | while ( -1 === self.className.indexOf( 'nav-menu' ) ) { 68 | 69 | // On li elements toggle the class .focus. 70 | if ( 'li' === self.tagName.toLowerCase() ) { 71 | if ( -1 !== self.className.indexOf( 'focus' ) ) { 72 | self.className = self.className.replace( ' focus', '' ); 73 | } else { 74 | self.className += ' focus'; 75 | } 76 | } 77 | 78 | self = self.parentElement; 79 | } 80 | } 81 | } )(); 82 | -------------------------------------------------------------------------------- /revealing-module-pattern-constructor.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Revealing Module Pattern with Constructor Boilerplate 3 | * (c) 2017 Chris Ferdinandi, MIT License, https://gomakethings.com 4 | */ 5 | var myPlugin = (function () { 6 | 7 | 'use strict'; 8 | 9 | // 10 | // Shared Variables 11 | // 12 | 13 | var defaults = {}; 14 | 15 | 16 | // 17 | // Shared Methods 18 | // 19 | 20 | /*! 21 | * Merge two or more objects together. 22 | * (c) 2017 Chris Ferdinandi, MIT License, https://gomakethings.com 23 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional] 24 | * @param {Object} objects The objects to merge together 25 | * @returns {Object} Merged values of defaults and options 26 | */ 27 | var extend = function () { 28 | 29 | // Variables 30 | var extended = {}; 31 | var deep = false; 32 | var i = 0; 33 | 34 | // Check if a deep merge 35 | if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) { 36 | deep = arguments[0]; 37 | i++; 38 | } 39 | 40 | // Merge the object into the extended object 41 | var merge = function (obj) { 42 | for (var prop in obj) { 43 | if (obj.hasOwnProperty(prop)) { 44 | // If property is an object, merge properties 45 | if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') { 46 | extended[prop] = extend(extended[prop], obj[prop]); 47 | } else { 48 | extended[prop] = obj[prop]; 49 | } 50 | } 51 | } 52 | }; 53 | 54 | // Loop through each object and conduct a merge 55 | for (; i < arguments.length; i++) { 56 | var obj = arguments[i]; 57 | merge(obj); 58 | } 59 | 60 | return extended; 61 | 62 | }; 63 | 64 | 65 | // 66 | // Constructor 67 | // Can be named anything you want 68 | // 69 | 70 | var Constructor = function (options) { 71 | 72 | // 73 | // Unique Variables 74 | // 75 | 76 | var publicAPIs = {}; 77 | var settings; 78 | 79 | 80 | // 81 | // Unique Methods 82 | // 83 | 84 | /** 85 | * A private method 86 | */ 87 | var somePrivateMethod = function () { 88 | // Code goes here... 89 | }; 90 | 91 | /** 92 | * A public method 93 | */ 94 | publicAPIs.doSomething = function () { 95 | somePrivateMethod(); 96 | // Code goes here... 97 | }; 98 | 99 | /** 100 | * Another public method 101 | */ 102 | publicAPIs.init = function (options) { 103 | 104 | // Merge options into defaults 105 | settings = extend(defaults, options || {}); 106 | 107 | // Code goes here... 108 | 109 | }; 110 | 111 | // Initialize the plugin 112 | publicAPIs.init(options); 113 | 114 | // Return the public APIs 115 | return publicAPIs; 116 | 117 | }; 118 | 119 | 120 | // 121 | // Return the constructor 122 | // 123 | 124 | return Constructor; 125 | 126 | })(); 127 | 128 | var myPluginInstance = new myPlugin({ 129 | mayo: true, 130 | bread: 'rye', 131 | }); 132 | -------------------------------------------------------------------------------- /event-polyfill.js: -------------------------------------------------------------------------------- 1 | // Polyfill event.preventDefault & event.addEventListener 2 | if (!window.addEventListener) { 3 | (function() { 4 | if (!Event.prototype.preventDefault) { 5 | Event.prototype.preventDefault=function() { 6 | this.returnValue=false; 7 | }; 8 | } 9 | if (!Event.prototype.stopPropagation) { 10 | Event.prototype.stopPropagation=function() { 11 | this.cancelBubble=true; 12 | }; 13 | } 14 | if (!Element.prototype.addEventListener) { 15 | var eventListeners=[]; 16 | var addEventListener=function(type,listener /*, useCapture (will be ignored) */) { 17 | var self=this; 18 | var wrapper=function(e) { 19 | e.target=e.srcElement; 20 | e.currentTarget=self; 21 | if (listener.handleEvent) { 22 | listener.handleEvent(e); 23 | } else { 24 | listener.call(self,e); 25 | } 26 | }; 27 | if (type=="DOMContentLoaded") { 28 | var wrapper2=function(e) { 29 | if (document.readyState=="complete") { 30 | wrapper(e); 31 | } 32 | }; 33 | document.attachEvent("onreadystatechange",wrapper2); 34 | eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper2}); 35 | if (document.readyState=="complete") { 36 | var e=new Event(); 37 | e.srcElement=window; 38 | wrapper2(e); 39 | } 40 | } else { 41 | this.attachEvent("on"+type,wrapper); 42 | eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper}); 43 | } 44 | }; 45 | var removeEventListener=function(type,listener /*, useCapture (will be ignored) */) { 46 | var counter=0; 47 | while (counter p.offsetWidth + p.scrollLeft || 75 | //-- If the target element is to the left of the parent elm 76 | l + w - VISIBLE_PADDING < p.scrollLeft || 77 | //-- If the target element is under the parent elm 78 | t + VISIBLE_PADDING > p.offsetHeight + p.scrollTop || 79 | //-- If the target element is above the parent elm 80 | t + h - VISIBLE_PADDING < p.scrollTop 81 | ) { 82 | //-- Our target element is out of bounds: 83 | return false; 84 | } 85 | } 86 | //-- Add the offset parent's left/top coords to our element's offset: 87 | if ( el.offsetParent === p ) { 88 | l += p.offsetLeft; 89 | t += p.offsetTop; 90 | } 91 | //-- Let's recursively check upwards: 92 | return _isVisible(p, t, r, b, l, w, h); 93 | } 94 | return true; 95 | } 96 | 97 | //-- Cross browser method to get style properties: 98 | function _getStyle(el, property) { 99 | if ( window.getComputedStyle ) { 100 | return document.defaultView.getComputedStyle(el,null)[property]; 101 | } 102 | if ( el.currentStyle ) { 103 | return el.currentStyle[property]; 104 | } 105 | } 106 | 107 | function _elementInDocument(element) { 108 | while (element = element.parentNode) { 109 | if (element == document) { 110 | return true; 111 | } 112 | } 113 | return false; 114 | } 115 | 116 | return _isVisible(this); 117 | 118 | }; -------------------------------------------------------------------------------- /stickyFooter.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if ( typeof define === 'function' && define.amd ) { 3 | define([], factory(root)); 4 | } else if ( typeof exports === 'object' ) { 5 | module.exports = factory(root); 6 | } else { 7 | root.stickyFooter = factory(root); 8 | } 9 | })(typeof global !== 'undefined' ? global : this.window || this.global, function (root) { 10 | 11 | 'use strict'; 12 | 13 | // 14 | // Variables 15 | // 16 | 17 | var stickyFooter = {}; // Object for public APIs 18 | var supports = 'querySelector' in document && 'addEventListener' in root; // Feature test 19 | var settings, wrap, footer, eventTimeout; 20 | 21 | // Default settings 22 | var defaults = { 23 | selectorWrap: '[data-sticky-wrap]', 24 | selectorFooter: '[data-sticky-footer]', 25 | callback: function () {} 26 | }; 27 | 28 | 29 | // 30 | // Methods 31 | // 32 | 33 | /** 34 | * Merge two or more objects. Returns a new object. 35 | * @private 36 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional] 37 | * @param {Object} objects The objects to merge together 38 | * @returns {Object} Merged values of defaults and options 39 | */ 40 | var extend = function () { 41 | 42 | // Variables 43 | var extended = {}; 44 | var deep = false; 45 | var i = 0; 46 | var length = arguments.length; 47 | 48 | // Check if a deep merge 49 | if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) { 50 | deep = arguments[0]; 51 | i++; 52 | } 53 | 54 | // Merge the object into the extended object 55 | var merge = function (obj) { 56 | for ( var prop in obj ) { 57 | if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) { 58 | // If deep merge and property is an object, merge properties 59 | if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) { 60 | extended[prop] = extend( true, extended[prop], obj[prop] ); 61 | } else { 62 | extended[prop] = obj[prop]; 63 | } 64 | } 65 | } 66 | }; 67 | 68 | // Loop through each object and conduct a merge 69 | for ( ; i < length; i++ ) { 70 | var obj = arguments[i]; 71 | merge(obj); 72 | } 73 | 74 | return extended; 75 | 76 | }; 77 | 78 | /** 79 | * Get height of the viewport 80 | * @private 81 | * @return {Number} Height of the viewport in pixels 82 | */ 83 | var getViewportHeight = function () { 84 | return Math.max( document.documentElement.clientHeight, window.innerHeight || 0 ); 85 | }; 86 | 87 | /** 88 | * Set page wrapper height to fill viewport (minus footer height) 89 | * @private 90 | * @param {Element} wrap Page wrapper 91 | * @param {Element} footer Page footer 92 | * @param {Object} settings 93 | */ 94 | var setWrapHeight = function ( wrap, footer, settings ) { 95 | wrap.style.minHeight = ( getViewportHeight() - footer.offsetHeight ) + 'px'; 96 | settings.callback(); // Run callback 97 | }; 98 | 99 | /** 100 | * Destroy the current initialization. 101 | * @public 102 | */ 103 | stickyFooter.destroy = function () { 104 | 105 | if ( !settings ) return; 106 | 107 | // Unset styles 108 | document.documentElement.style.minHeight = ''; 109 | document.body.style.minHeight = ''; 110 | wrap.style.minHeight = ''; 111 | window.removeEventListener( 'resize', eventThrottler, false ); 112 | 113 | // Reset variables 114 | settings = null; 115 | wrap = null; 116 | footer = null; 117 | eventTimeout = null; 118 | 119 | }; 120 | 121 | /** 122 | * On window scroll and resize, only run events at a rate of 15fps for better performance 123 | * @private 124 | * @param {Function} eventTimeout Timeout function 125 | * @param {NodeList} wrap The content wrapper for the page 126 | * @param {NodeList} footer The footer for the page 127 | * @param {Object} settings 128 | */ 129 | var eventThrottler = function () { 130 | if ( !eventTimeout ) { 131 | eventTimeout = setTimeout(function() { 132 | eventTimeout = null; 133 | setWrapHeight( wrap, footer, settings ); 134 | }, 66); 135 | } 136 | }; 137 | 138 | /** 139 | * Initialize Plugin 140 | * @public 141 | * @param {Object} options User settings 142 | */ 143 | stickyFooter.init = function ( options ) { 144 | 145 | // feature test 146 | if ( !supports ) return; 147 | 148 | // Destroy any existing initializations 149 | stickyFooter.destroy(); 150 | 151 | // Selectors and variables 152 | settings = extend( defaults, options || {} ); // Merge user options with defaults 153 | wrap = document.querySelector( settings.selectorWrap ); 154 | footer = document.querySelector( settings.selectorFooter ); 155 | 156 | // Sanity check 157 | if ( !wrap || !footer ) return; 158 | 159 | // Stick footer 160 | document.documentElement.style.minHeight = '100%'; 161 | document.body.style.minHeight = '100%'; 162 | setWrapHeight( wrap, footer, settings ); 163 | window.addEventListener( 'resize', eventThrottler, false); // Run Sticky Footer on window resize 164 | 165 | }; 166 | 167 | 168 | // 169 | // Public APIs 170 | // 171 | 172 | return stickyFooter; 173 | 174 | }); -------------------------------------------------------------------------------- /classList-polyfill.js: -------------------------------------------------------------------------------- 1 | /* 2 | * classList.js: Cross-browser full element.classList implementation. 3 | * 2014-07-23 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * Public Domain. 7 | * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | */ 9 | 10 | /*global self, document, DOMException */ 11 | 12 | /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/ 13 | 14 | if ("document" in self) { 15 | 16 | // Full polyfill for browsers with no classList support 17 | if (!("classList" in document.createElement("_"))) { 18 | 19 | (function (view) { 20 | 21 | "use strict"; 22 | 23 | if (!('Element' in view)) return; 24 | 25 | var 26 | classListProp = "classList" 27 | , protoProp = "prototype" 28 | , elemCtrProto = view.Element[protoProp] 29 | , objCtr = Object 30 | , strTrim = String[protoProp].trim || function () { 31 | return this.replace(/^\s+|\s+$/g, ""); 32 | } 33 | , arrIndexOf = Array[protoProp].indexOf || function (item) { 34 | var 35 | i = 0 36 | , len = this.length 37 | ; 38 | for (; i < len; i++) { 39 | if (i in this && this[i] === item) { 40 | return i; 41 | } 42 | } 43 | return -1; 44 | } 45 | // Vendors: please allow content code to instantiate DOMExceptions 46 | , DOMEx = function (type, message) { 47 | this.name = type; 48 | this.code = DOMException[type]; 49 | this.message = message; 50 | } 51 | , checkTokenAndGetIndex = function (classList, token) { 52 | if (token === "") { 53 | throw new DOMEx( 54 | "SYNTAX_ERR" 55 | , "An invalid or illegal string was specified" 56 | ); 57 | } 58 | if (/\s/.test(token)) { 59 | throw new DOMEx( 60 | "INVALID_CHARACTER_ERR" 61 | , "String contains an invalid character" 62 | ); 63 | } 64 | return arrIndexOf.call(classList, token); 65 | } 66 | , ClassList = function (elem) { 67 | var 68 | trimmedClasses = strTrim.call(elem.getAttribute("class") || "") 69 | , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] 70 | , i = 0 71 | , len = classes.length 72 | ; 73 | for (; i < len; i++) { 74 | this.push(classes[i]); 75 | } 76 | this._updateClassName = function () { 77 | elem.setAttribute("class", this.toString()); 78 | }; 79 | } 80 | , classListProto = ClassList[protoProp] = [] 81 | , classListGetter = function () { 82 | return new ClassList(this); 83 | } 84 | ; 85 | // Most DOMException implementations don't allow calling DOMException's toString() 86 | // on non-DOMExceptions. Error's toString() is sufficient here. 87 | DOMEx[protoProp] = Error[protoProp]; 88 | classListProto.item = function (i) { 89 | return this[i] || null; 90 | }; 91 | classListProto.contains = function (token) { 92 | token += ""; 93 | return checkTokenAndGetIndex(this, token) !== -1; 94 | }; 95 | classListProto.add = function () { 96 | var 97 | tokens = arguments 98 | , i = 0 99 | , l = tokens.length 100 | , token 101 | , updated = false 102 | ; 103 | do { 104 | token = tokens[i] + ""; 105 | if (checkTokenAndGetIndex(this, token) === -1) { 106 | this.push(token); 107 | updated = true; 108 | } 109 | } 110 | while (++i < l); 111 | 112 | if (updated) { 113 | this._updateClassName(); 114 | } 115 | }; 116 | classListProto.remove = function () { 117 | var 118 | tokens = arguments 119 | , i = 0 120 | , l = tokens.length 121 | , token 122 | , updated = false 123 | , index 124 | ; 125 | do { 126 | token = tokens[i] + ""; 127 | index = checkTokenAndGetIndex(this, token); 128 | while (index !== -1) { 129 | this.splice(index, 1); 130 | updated = true; 131 | index = checkTokenAndGetIndex(this, token); 132 | } 133 | } 134 | while (++i < l); 135 | 136 | if (updated) { 137 | this._updateClassName(); 138 | } 139 | }; 140 | classListProto.toggle = function (token, force) { 141 | token += ""; 142 | 143 | var 144 | result = this.contains(token) 145 | , method = result ? 146 | force !== true && "remove" 147 | : 148 | force !== false && "add" 149 | ; 150 | 151 | if (method) { 152 | this[method](token); 153 | } 154 | 155 | if (force === true || force === false) { 156 | return force; 157 | } else { 158 | return !result; 159 | } 160 | }; 161 | classListProto.toString = function () { 162 | return this.join(" "); 163 | }; 164 | 165 | if (objCtr.defineProperty) { 166 | var classListPropDesc = { 167 | get: classListGetter 168 | , enumerable: true 169 | , configurable: true 170 | }; 171 | try { 172 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 173 | } catch (ex) { // IE 8 doesn't support enumerable:true 174 | if (ex.number === -0x7FF5EC54) { 175 | classListPropDesc.enumerable = false; 176 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 177 | } 178 | } 179 | } else if (objCtr[protoProp].__defineGetter__) { 180 | elemCtrProto.__defineGetter__(classListProp, classListGetter); 181 | } 182 | 183 | }(self)); 184 | 185 | } else { 186 | // There is full or partial native classList support, so just check if we need 187 | // to normalize the add/remove and toggle APIs. 188 | 189 | (function () { 190 | "use strict"; 191 | 192 | var testElement = document.createElement("_"); 193 | 194 | testElement.classList.add("c1", "c2"); 195 | 196 | // Polyfill for IE 10/11 and Firefox <26, where classList.add and 197 | // classList.remove exist but support only one argument at a time. 198 | if (!testElement.classList.contains("c2")) { 199 | var createMethod = function(method) { 200 | var original = DOMTokenList.prototype[method]; 201 | 202 | DOMTokenList.prototype[method] = function(token) { 203 | var i, len = arguments.length; 204 | 205 | for (i = 0; i < len; i++) { 206 | token = arguments[i]; 207 | original.call(this, token); 208 | } 209 | }; 210 | }; 211 | createMethod('add'); 212 | createMethod('remove'); 213 | } 214 | 215 | testElement.classList.toggle("c3", false); 216 | 217 | // Polyfill for IE 10 and Firefox <24, where classList.toggle does not 218 | // support the second argument. 219 | if (testElement.classList.contains("c3")) { 220 | var _toggle = DOMTokenList.prototype.toggle; 221 | 222 | DOMTokenList.prototype.toggle = function(token, force) { 223 | if (1 in arguments && !this.contains(token) === !force) { 224 | return force; 225 | } else { 226 | return _toggle.call(this, token); 227 | } 228 | }; 229 | 230 | } 231 | 232 | testElement = null; 233 | }()); 234 | 235 | } 236 | 237 | } --------------------------------------------------------------------------------