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.