3
\$\begingroup\$

I am trying to learn how to write more object oriented javascript code, but I am having a hard time understanding the correct way to do it, and how it is useful for front end development. As you can see below, I tried to rewrite my old javascript code to be more object oriented, but it just doesn't seem necessary for what I am doing. I am not even sure if I am doing it correctly (would it be better to use a self executing anonymous function for this?). Could someone take a look at my code and tell me how I can improve it? Also, explain to me why it is important to write object oriented javascript for the development of a websites frontend?

Original Code

// Document and Windows Variables
var $document = $(document),
    $window = $(window);

// Equalize
function equal_heights() {
    var $row = $('.equal');

    if ($row.length > 0) {
        $row.each(function() {
            var $sections = $(this).children(),
                largest_height = 0;

            $sections.height('auto');

            $sections.each(function() {
                var h = $(this).height();
                if (largest_height < h) largest_height = h;
            });

            $sections.height(largest_height);
        });
    }
}

// Position Popups
function position_popup() {
    var $popup = $('.popup'),
        pop_height = $popup.outerHeight(true),
        pop_width = $popup.outerWidth(true),
        browser_height = $(window).height(),
        pop_top = 10,
        page_pos = document.documentElement.scrollTop || document.body.scrollTop;

    if (pop_height < browser_height) {
        pop_top = browser_height / 2 - pop_height / 2;
    }

    $popup.css({
        top: page_pos + pop_top
    });
}

// Page Animation Fade (On Scroll)
function page_animations() {
    $('.fade-me').each(function(i) {
        if ($window.scrollTop() > $(this).offset().top - ($window.height() / 1)) {

            $(this).addClass('is-showing');

        }
    });
}

page_animations();

/********************
Run Functions
********************/
$window.on({
  load: function() {
   equal_heights();
   position_popup();
  }
});

OOP

// Document and Windows Variables
var $reuseVars = {
    window: $(window),
    document: $(document)
}

//  Functions Object
function AllFunctions() {}

AllFunctions.prototype = {
    // Equal Heights
    equalHeights: function() {
        var $row = $('.equal');

        if ($row.length > 0) {
            $row.each(function() {
                var $sections = $(this).children(),
                    largest_height = 0;

                $sections.height('auto');

                $sections.each(function() {
                    var h = $(this).height();
                    if (largest_height < h) largest_height = h;
                });

                $sections.height(largest_height);
            });
        }
    },

    // Popup Position
    positionPopup: function() {
        var $popup = $('.popup'),
            pop_height = $popup.outerHeight(true),
            pop_width = $popup.outerWidth(true),
            browser_height = $(window).height(),
            pop_top = 10,
            page_pos = document.documentElement.scrollTop || document.body.scrollTop;

        if (pop_height < browser_height) {
            pop_top = browser_height / 2 - pop_height / 2;
        }

        $popup.css({
            top: page_pos + pop_top
        });
    },

    // Page Fade Animation - On Scroll
    pageAnimation: function() {
        $('.fade-me').each(function(i) {
            if ($reuseVars.window.scrollTop() > $(this).offset().top - ($reuseVars.window.height() / 1)) {

                $(this).addClass('is-showing');

            }
        });
    }
};

// Create All Functions Object
var allFunctions = new AllFunctions();

// Run Functions on doc ready
allFunctions.pageAnimation();

/********************
 Run Functions
********************/
$reuseVars.window.on({
   load: function() {
     allFunctions.equalHeights();
     allFunctions.positionPopup();
   }
});
\$\endgroup\$
3
\$\begingroup\$

I think you are over-engineering here. OOP is not an end in itself, so don't convert your code to "OOP style" just because.

The most desirable property of code (after correctness) is clarity. Clarity means obviousness, which means ease of maintenance, both of which mean reduced chance of bugs. To me, your second code sample is less clear than your first.

I would go into a different direction and just try to simplify your first code sample instead of slapping some fancy "OOP" stuff on.

Part of simplification is abstracting functionality into re-usable blocks. jQuery is very easy to extend with your own functions, so let's create two extensions maxHeight and positionCenter:

$.fn.extend({
    maxHeight: function () {
        var heights = this.map(function() { return $(this).height(); });
        return Math.max.apply(null, heights);
    },
    positionCenter: function () {
        return this.each(function () {
            var $this = $(this),
                height = $this.outerHeight(true),
                width = $this.outerWidth(true),
                browser_height = $(window).height(),
                top = 10,
                page_pos = document.documentElement.scrollTop || document.body.scrollTop;

            if (height < browser_height) {
                top = browser_height / 2 - height / 2;
            }
            $this.css({
              top: page_pos + top
              // "left" calculation is still missing
            });
        });
    }
});

This could end up in a script file where you collect all your custom extensions.


With some of the plumbing out of the way, the code that actually works on the page becomes a whole lot clearer:

$(window).scroll(function () {
    var $window = $(window);
    $('.fade-me').each(function() {
        var $this = $(this);
        if ($window.scrollTop() > $this.offset().top - $window.height() / 1) {
            $this.addClass('is-showing');
            // when is this class removed?
        }
    });
});

$(function () {
    $('.equal').each(function() {
        var $sections = $(this).children().height('auto');
        $sections.height( $sections.maxHeight() );
    });
    $('.popup').positionCenter();
});

(The above is of course untested, you might have to tweak it a little here and there.)


The next thing you could investigate is making your own custom jQuery selectors.

Imagine your code scroll handler would look like this:

$(window).scroll(function () {
    $('.fade-me:inview').addClass('is-showing');
});

Wouldn't that be even clearer?

Here is how to do that: http://www.sitepoint.com/make-your-own-custom-jquery-selector/


As a finger exercise, write a jQuery extension named equalizeHeight, such that you can make the following simplification:

$('.equal').each(function() {
    $(this).children().equalizeHeight();
});
\$\endgroup\$
  • \$\begingroup\$ Wow, this is very helpful! This is much cleaner than my code. I will definitely look into making my own custom jQuery selectors. I had no idea that you could do this. I am still kinda new to this...I only have about 1 year of professional experience, so I am still learning a lot. I really appreciate this. Do you think that object oriented programming is useful for development the Frontend of a website, or does it just depend on what you're trying to do? \$\endgroup\$ – Christopher Hill Apr 13 '16 at 17:53
  • \$\begingroup\$ That's the wrong question to ask. Those two things are not interrelated. First off, JavaScript is an object-oriented language, so technically speaking you are already doing OOP. Creating your own objects is useful when that increases clarity, i.e. when it makes it easier to reason about your code. It's the same goal as stated above. The context (UI, Backend, whatever) has nothing to do with that at all. \$\endgroup\$ – Tomalak Apr 13 '16 at 18:03
  • \$\begingroup\$ @Christopher BTW, I've added a homework assignment of sorts. See modified question. It's not strictly necessary to take it to that kind of level, but it conveys what I mean by clarity. \$\endgroup\$ – Tomalak Apr 13 '16 at 18:12
  • \$\begingroup\$ Ok... I am trying to figure this out. It may take me a few minutes. \$\endgroup\$ – Christopher Hill Apr 13 '16 at 18:30
  • \$\begingroup\$ $.fn.extend({ equalizeHeight: function() { var $sections = $(this).height('auto'); $sections.height($sections.maxHeight()); } }); Would this work? \$\endgroup\$ – Christopher Hill Apr 13 '16 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.