Funciones básicas alternativas para transitar desde jQuery a Vanilla JavaScript

Supongo que no soy el único que empezó a usar JavaScript por medio de jQuery. La librería jQuery ha sido crucial para el desarrollo en Front, nos ha permitido a los desarrolladores hacer casi todo lo que queríamos de una manera rápida, sencilla y sin preocuparse por los navegadores. El único inconveniente que tenía era que debíamos de cargar la librería, que no era especialmente ligera, y que ralentizaba la carga y uso de la web en cuestión.

Gracias al desarrollo del JavaScript «a pelo» (llamado Vanilla JS, o Plain JS) ahora ya podemos prescindir de jQuery y seguir disfrutando de la facilidad de uso, pero aún así a mi me está costando dar el salto de jQuery a VanillaJS, y es que son ya muchos años trabando del modo jQuery.

jquery_to_js

Para intentar aliviar este cambio, he recopilado una sería de funciones que ya usaba de manera recursiva en jQuery, que hacen que mi código JS sea más limpio y asemejarlo un poco más a mi quería librearía.

He juntado todas ellas (y otras que vendrán) en un fichero que enlazo en mis proyectos, y así puedo hacer referencia a estas funciones a lo largo de todo el proyecto.

<script src="/js/plainJS.js" />

Seleccionar elementos del DOM

Esta es una de las tareas que más veces debemos de hacer, y con jQuery era tan sencillo como usar la función $. Ahora con Javascript podemos hacer uso de toda esta potencia por medio de document.querySelector y document.querySelectorAll, pero debemos de escribir muchos caracteres cada vez que queremos usarlo. Por eso podemos crear una función $ y $1 (o como tu quieras llamarla) que te haga ser más eficiente.

// Seleccionar una lista de elementos por medio de un selector CSS. El contexto es opcional
function $(selector, context) {
    return (context || document).querySelectorAll(selector);
}

// Seleccionar el primer elemento que tenga el selector CSS. El contexto es opcional:
function $1(selector, context) {
    return (context || document).querySelector(selector);
}

Ejemplos:

// Seleccionar todos los elementos '.bar' dentro de contenedores '.foo'
var elems = $('.foo .bar');

// ejemplo con contexto
var container = $1('.foo');
// seleccionar elementos '.bar' dentro del contenedor
var elems = $('.bar', container);

fuente.

Añadir, eliminar y comprobar clases

function hasClass(el, className) {
    return el.classList ? el.classList.contains(className) : new RegExp('\\b'+ className+'\\b').test(el.className);
}

function addClasses(el, classesArray) {
    if ( classesArray instanceof Array ) {
        for ( var i = 0; i < classesArray.length; i++ ) { addClass(el, classesArray[i]); } } else { addClass(el, classesArray); } } function addClass(el, className) { if ( el ) { if ( el.tagName ) { doAddClass(el, className); } else if ( el.length >= 1 ) {
            for ( var i = 0; i < el.length; i++ ) {
                doAddClass(el[i], className);
            }
        }
    }
}

function removeClasses(el, classesArray) {
    if ( classesArray instanceof Array ) {
        for ( var i = 0; i < classesArray.length; i++ ) { removeClass(el, classesArray[i]); } } else { removeClass(el, classesArray); } } function removeClass(el, className) { if ( el ) { if ( el.tagName ) { doRemoveClass(el, className); } else if ( el.length >= 1 ) {
            for ( var i = 0; i < el.length; i++ ) {
                doRemoveClass(el[i], className);
            }
        }
    }
}

function doAddClass(el, className) {
    if (el.classList) {
        el.classList.add(className);
    } else if (!hasClass(el, className)) {
        el.className += ' ' + className;
    }
}

function doRemoveClass(el, className) {
    if (el.classList) el.classList.remove(className);
    else el.className = el.className.replace(new RegExp('\\b'+ className+'\\b', 'g'), '');
}

function toggleClass(el, className) {
    if ( hasClass(el, className) ) {
        removeClass(el, className);
        return false;
    } else {
        addClass(el, className);
        return true;
    }
}

Ejemplos:

var el = document.querySelector('div');
if (!hasClass(el, 'foo')){
    addClass(el, 'foo');
}

fuente.

Manipular el DOM

function appendTo(wrap, newElem) {
    wrap.insertAdjacentHTML( 'beforeend', newElem);
}

function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

// document.getElementById("my-element").remove();
Element.prototype.remove = function() {
    this.parentElement.removeChild(this);
};

// document.getElementsByClassName("my-elements").remove();
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
    for(var i = this.length - 1; i >= 0; i--) {
        if(this[i] && this[i].parentElement) {
            this[i].parentElement.removeChild(this[i]);
        }
    }
};

// know if parents has a selector
// var parents = getParents(elem, '.some-class');
// var allParents = getParents(elem.parentNode);
var getParents = function ( elem, selector ) {

	// Element.matches() polyfill
	if (!Element.prototype.matches) {
		Element.prototype.matches =
			Element.prototype.matchesSelector ||
			Element.prototype.mozMatchesSelector ||
			Element.prototype.msMatchesSelector ||
			Element.prototype.oMatchesSelector ||
			Element.prototype.webkitMatchesSelector ||
			function(s) {
				var matches = (this.document || this.ownerDocument).querySelectorAll(s),
					i = matches.length;
				while (--i >= 0 && matches.item(i) !== this) {}
				return i > -1;
			};
	}

	// Setup parents array
	var parents = [];

	// Get matching parent elements
	for ( ; elem && elem !== document; elem = elem.parentNode ) {

		// Add matching parents to array
		if ( selector ) {
			if ( elem.matches( selector ) ) {
				parents.push( elem );
			}
		} else {
			parents.push( elem );
		}

	}

	return parents;

};

Obtener posición

function offset(el, parentElem) {
    var rect = el.getBoundingClientRect(),
        scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
        scrollTop = window.pageYOffset || document.getElementById(parentElem).scrollTop || document.documentElement.scrollTop;

    return { top: rect.top + scrollTop, left: rect.left + scrollLeft }
}

Ocultar o mostrar elementos

function hide(el) {
    el.style.display = 'none';
}

function show(el, value) {
    el.style.display = value;
}

function toggleVisibility(el, value) {
    var display = (window.getComputedStyle ? getComputedStyle(el, null) : el.currentStyle).display;
    if (display == 'none') el.style.display = value;
    else el.style.display = 'none';
}

Ejemplos:

hide( elem );
show( elem, 'block' );
toggleVisibility( elem, 'block' );

fuente.

Poner estilos CSS a un elemento

function css(el, styles) {
    for (var property in styles) {
        el.style[property] = styles[property];
    }
}

Ejemplos:

var el = document.querySelector('div');
css(el, { background: 'green', display: 'none', 'border-radius': '5px' });

fuente.

Animar una propiedad del elemento

// fade an element from the current state to full opacity in "duration" ms
function fadeOut(el, duration) {
    var s = el.style, step = 25/(duration || 300);
    s.opacity = s.opacity || 1;
    (function fade() {
        if ( ( s.opacity -= step) &amp;amp;amp;amp;amp;lt; 0 ) { s.display = "none"; } else { setTimeout(fade, 25); } })(); } // fade out an element from the current state to full transparency in "duration" ms // display is the display style the element is assigned after the animation is done function fadeIn(el, duration, display) { var s = el.style, step = 25/(duration || 300); s.opacity = s.opacity || 0; s.display = display || "block"; (function fade() { if ( ( s.opacity = parseFloat(s.opacity)+step) &amp;amp;amp;amp;amp;gt; 1 ) {
            s.opacity = 1;
        } else {
            setTimeout(fade, 25);
        }
    })();
}

Ejemplos:

fadeOut(elem, 500);
fadeIn(elem, 500);

fuente.

Peticiones Ajax

function getAjax(url, success) {
    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
    xhr.open('GET', url);
    xhr.onreadystatechange = function() {
        if (xhr.readyState>3 && xhr.status==200) success(xhr.responseText);
    };
    //xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.send();
    return xhr;
}

function getCORS(url, success) {
    var xhr = new XMLHttpRequest();
    if (!('withCredentials' in xhr)) xhr = new XDomainRequest(); // fix IE8/9
    xhr.open('GET', url);
    xhr.onload = success;
    xhr.send();
    return xhr;
}

function postAjax(url, data, success) {
    // example request
    // postAjax('http://foo.bar/', 'p1=1&p2=Hello+World', function(data){ console.log(data); });

    // example request with data object
    // postAjax('http://foo.bar/', { p1: 1, p2: 'Hello World' }, function(data){ console.log(data); });

    var params = typeof data == 'string' ? data : Object.keys(data).map(
        function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }
    ).join('&');

    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
    xhr.open('POST', url);
    xhr.onreadystatechange = function() {
        if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); }
    };
    //xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send(params);
    return xhr;
}

Ejemplos:

getAjax('http://foo.bar/?p1=1&amp;amp;amp;amp;amp;amp;p2=Hello+World', function(respone){
    var jsonData = JSON.parse(respone);
    console.log(respone);
    console.log(jsonData);
});

// example request
postAjax('http://foo.bar/', 'p1=1&amp;amp;amp;amp;amp;amp;p2=Hello+World', function(data){ console.log(data); });

// example request with data object
postAjax('http://foo.bar/', { p1: 1, p2: 'Hello World' }, function(data){ console.log(data); });

fuente.

Mergear dos objetos

function extend(obj, src) {
    Object.keys(src).forEach(function(key) { obj[key] = src[key]; });
    return obj;
}

Crear, obtener y borrar Cookies

function getCookie(name) {
    var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
    return v ? v[2] : null;
}

function setCookie(name, value, days) {
    var d = new Date();
    d.setTime(d.getTime() + 24*60*60*1000*days);
    document.cookie = name + "=" + value + ";path=/;expires=" + d.toGMTString();
}

function deleteCookie(name) { setCookie(name, '', -1); }

fuente.

Más información

También puedes consultar otras webs que tratan esta transición dulce con más profundidad: