Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

JavaScript

Brad Woods
Brad Woods
13,772 Points

Inspiration for Article Intro Effects - Deciphering Code

After watching the Treehouse show I've been trying to work out the JavaScript for 'Inspiration for Article Intro Effect' - http://tympanus.net/codrops/2014/05/22/inspiration-for-article-intro-effects/. The code is below - (function() {

            // detect if IE : from http://stackoverflow.com/a/16657946  
                // Thus, to detect IE:
                // if (ie) {}
                // And to detect the version:
                // ie === 6 // IE6
                // ie > 7 // IE8, IE9, IE10 ...
                // ie < 9 // Anything less than IE9 
            var ie = (function(){
                var undef,rv = -1; // Return value assumes failure.
                var ua = window.navigator.userAgent;
                var msie = ua.indexOf('MSIE ');
                var trident = ua.indexOf('Trident/');

                if (msie > 0) {
                    // IE 10 or older => return version number
                    rv = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
                } else if (trident > 0) {
                    // IE 11 (or newer) => return version number
                    var rvNum = ua.indexOf('rv:');
                    rv = parseInt(ua.substring(rvNum + 3, ua.indexOf('.', rvNum)), 10);
                }

                return ((rv > -1) ? rv : undef);
            }());


            // disable/enable scroll (mousewheel and keys) from http://stackoverflow.com/a/4770179                  
                // left: 37, up: 38, right: 39, down: 40, spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36
            var keys = [32, 37, 38, 39, 40], wheelIter = 0;

            function preventDefault(e) {
                e = e || window.event;
                if (e.preventDefault)
                e.preventDefault();
                e.returnValue = false;  
            }

            function keydown(e) {
                for (var i = keys.length; i--;) {
                    if (e.keyCode === keys[i]) {
                        preventDefault(e);
                        return;
                    }
                }
            }

            function touchmove(e) {
                preventDefault(e);
            }

            function wheel(e) {
                // for IE 
                //if( ie ) {
                    //preventDefault(e);
                //}
            }

            function disable_scroll() {
                window.onmousewheel = document.onmousewheel = wheel;
                document.onkeydown = keydown;
                document.body.ontouchmove = touchmove;
            }

            function enable_scroll() {
                window.onmousewheel = document.onmousewheel = document.onkeydown = document.body.ontouchmove = null;  
            }

            var docElem = window.document.documentElement,
                scrollVal,
                isRevealed, 
                noscroll, 
                isAnimating,
                container = document.getElementById( 'container' ),
                trigger = container.querySelector( 'button.trigger' );

            function scrollY() {
                return window.pageYOffset || docElem.scrollTop;
            }

            function scrollPage() {
                scrollVal = scrollY();

                if( noscroll && !ie ) {
                    if( scrollVal < 0 ) return false;
                    // keep it that way
                    window.scrollTo( 0, 0 );
                }

                if( classie.has( container, 'notrans' ) ) {
                    classie.remove( container, 'notrans' );
                    return false;
                }

                if( isAnimating ) {
                    return false;
                }

                if( scrollVal <= 0 && isRevealed ) {
                    toggle(0);
                }
                else if( scrollVal > 0 && !isRevealed ){
                    toggle(1);
                }
            }

            function toggle( reveal ) {
                isAnimating = true;

                if( reveal ) {
                    classie.add( container, 'modify' );
                }
                else {
                    noscroll = true;
                    disable_scroll();
                    classie.remove( container, 'modify' );
                }

                // simulating the end of the transition:
                setTimeout( function() {
                    isRevealed = !isRevealed;
                    isAnimating = false;
                    if( reveal ) {
                        noscroll = false;
                        enable_scroll();
                    }
                }, 600 );
            }

            // refreshing the page...
            var pageScroll = scrollY();
            noscroll = pageScroll === 0;

            disable_scroll();

            if( pageScroll ) {
                isRevealed = true;
                classie.add( container, 'notrans' );
                classie.add( container, 'modify' );
            }

            window.addEventListener( 'scroll', scrollPage );
            trigger.addEventListener( 'click', function() { toggle( 'reveal' ); } );
        })();
    </script>

I'm not sure how this section of the code works - function preventDefault(e) { e = e || window.event; if (e.preventDefault) e.preventDefault(); e.returnValue = false;
}

I can't see where the function is being called or what is being passed into it. I understand the method 'e.preventDefault' but don't understand how it is being used here. Can anyone help? I'm keen to figure this script.

2 Answers

Older versions of Internet Explorer handled events differently. They didn't pass the event object into the handler function, instead, you'd have to check the window.event property to see what the event was.

They also had a different mechanism for preventing the default reaction to the event, instead of the preventDefault method, you'd use e.returnValue = false.

This prevent function basically does all of that in one place, to keep the other event handling methods clean (keydown, touchmove, wheel).

function preventDefault(e) { 
   e = e || window.event; // if an event object was passed, use that object, otherwise get it from window.event
   if (e.preventDefault) { // if the browser supports e.preventDefault() use that
      e.preventDefault(); 
   }
   e.returnValue = false; // in cases where preventDefault is not available, set e.returnValue to false
}

So, instead of having to write this piece of code for every handler, it was refactored into a function you can recycle throughout the rest of the code.

Brad Woods
Brad Woods
13,772 Points

Could you detail how the event object is being passed into the function without an event handler preceding it?

Those three functions I mentioned are set as event handlers in the disable_scroll() function:

function disable_scroll() {
   window.onmousewheel = document.onmousewheel = wheel;
   document.onkeydown = keydown;
   document.body.ontouchmove = touchmove;
}

The browser automatically sends event objects to event handlers. That object gets passed on by those functions to the preventDefault function.

Brad Woods
Brad Woods
13,772 Points

Do you have any idea why this was written without using JQuery. My understanding is that JQuery makes interacting with the DOM a lot easier/ produces more efficient code.

jQuery makes it easier to write code, and the overall code is shorter but it's not really more efficient. Think about it, jQuery is a fairly large library (241.55 KB uncompressed for version 2.1.1). The user's browser will have to parse all of jQuery before it starts with this script.

This particular script is roughly 3 KB. Let's say the jQuery version is half of that — 1.5 KB. That sounds great, but the user will have to download whole jQuery on top of that — over 243 KB for a handful of effects that amount to 3 KB of vanilla JavaScript.

Sure, computers are fast these days, and Internet speeds are decent all over the world, but throw in the mobile users and things change a bit.

I always avoid jQuery unless I really need it. There are fewer browser quirks, they adhere to standards more than ever and in many cases you can easily write vanilla JavaScript.

Even when I use jQuery, I often find that I have no idea how it does certain things. Then I have to go through the code of the library to figure out if that's really what I want.

Writing vanilla JavaScript will always be more optimised (if you know what you're doing), you'll know exactly how things work, your users' traffic will go down, as will your server's.

Frankly, the amount of JS libraries that depend on jQuery is ridiculous. Half of them require you to use jQuery because of one line of code that could be polyfilled with two additional lines of code. So you get stuck with a whole library full of stuff that you don't need (in most cases).