Vanilla

Recently, I’ve been working to remove jQuery dependencies on personal projects. I outlined some of that effort with the refactoring of my A-Z Directory module. But I turned my attention to my joomla.bmorecreativeinc.com site, which as you might have guessed, is a Joomla site which serves as my demonstration, documentation, and download repository for my three Joomla modules. I was using jQuery for some basic functionality, namely a scroll-to-top arrow in the site footer and a mobile navigation slideToggle animation.

For the scroll-to-top functionality, I followed Pawel Grzybek’s excellent tutorial on page scrolling in vanilla JavaScript. I pared it down to only include the necessary code for the scroll-to-top functionality:

document.addEventListener( 'DOMContentLoaded', function() {
// scroll top
document.querySelector( 'a .icon-chevron-up' ).addEventListener( 'click', function(){
scrollIt();
});
});
// https://pawelgrzybek.com/page-scroll-in-vanilla-javascript/
function scrollIt( duration = 500 ) {
// store initial position of window and time
const start = window.pageYOffset;
const startTime = 'now' in window.performance ? performance.now() : new Date().getTime();
// if requestAnimationFrame is not supported, move window to top without animation
if( 'requestAnimationFrame' in window === false ) {
window.scroll( 0, 0 );
return;
}
// function resolves position of window and moves to top
function scroll() {
const now = 'now' in window.performance ? performance.now() : new Date().getTime();
const time = Math.min( 1, ( ( now - startTime ) / duration ) );
window.scroll( 0, Math.ceil( ( time * ( 0 - start ) ) + start ) );
// stop requesting animation when window reaches top
if( window.pageYOffset === 0 ) {
return;
}
// if window still needs to scroll to reach destination, request another scroll invokation
requestAnimationFrame( scroll );
}
// invoke scroll and sequential requestAnimationFrame
scroll();
}

The great part of Pawel’s code is the check whether the browser supports requestAnimationFrame. If it does, the scroll-to-top animates; if not, the page simply goes to the top without animation.

For the slideToggle, I transferred the responsbility to a CSS transition. This technique only works because I know the height of the expanded menu.

.moduletable_nav{
overflow: hidden;
transition: height .3s linear;
width: 100%;
height: 0;
position: absolute;
top: 100px;
left: 0;
z-index: 99;
}
.active .moduletable_nav{
height: 146px;
}
document.addEventListener( 'DOMContentLoaded', function() {
// mobile nav
document.getElementById( 'nav-toggle' ).addEventListener( 'click', function(){
document.querySelector( 'header' ).classList.toggle( 'active' );
});
});

If the height is dynamic, check out this answer on Stack Overflow, in which the height is calculated with JavaScript using clientHeight.

The final step was to remove Joomla’s default loading of jQuery and some additional JavaScript libraries:

$doc = JFactory::getDocument();
unset( $doc->_scripts[JURI::root(true) . '/media/jui/js/jquery.min.js'] );
unset( $doc->_scripts[JURI::root(true) . '/media/jui/js/jquery-noconflict.js'] );
unset( $doc->_scripts[JURI::root(true) . '/media/jui/js/jquery-migrate.min.js'] );
unset( $doc->_scripts[JURI::root(true) . '/media/system/js/caption.js'] );
$this->_script = preg_replace( '%jQuery\(window\)\.on\(\'load\',\s*function\(\)\s*{\s*new\s*JCaption\(\'img.caption\'\);\s*}\);\s*%', '', $this->_script );
if( empty( $this->_script['text/javascript'] ) ) unset( $this->_script['text/javascript'] );

Now, I have all the benefits of Joomla, with its easy content and menu creation, but I’ve eliminated all render-blocking JavaScript in above-the-fold content and shed over 100 kB of JavaScript, dramatically improving load time and site performance.