2
\$\begingroup\$

I'm trying to reproduce the Google Schemer (schemer.com) effect they use with their buttons for indicating a user wanting to do something/already doing something. I more or less got it replicated but it seems like the amount of code is a bit much.

JSFiddle

Does it seem like too much code for something that seems to be so "simple"?

// WTD object
var userWTD = {
    numTimesWtd: 0,
    numTimesAdt: 0,
    wantsToDoIt: false,
    alreadyDidIt: false,
};

// Update WTD text when user clicks the button/link
$('.btn-wtd').on('click', function() {

    // User wants to undo their WTD
    if ($(this).hasClass('btn-wtd-active')) {
        $(this).removeClass('btn-wtd-active btn-info');

        // Decrement # times user wants to do it
        if (userWTD.numTimesWtd > 0)--userWTD.numTimesWtd;
        else userWTD.numTimesWtd = 0;

        // user has not wanted to do it
        if (userWTD.numTimesWtd == 0) {
            // ...and already did it once
            if (userWTD.numTimesAdt == 1) {
                $('.btn-adt').addClass('btn-adt-active');
                $('.want-again-text, .adt-text').show();
                $('.unwant-text, .want-text, .done-it-text, .done-again-text').hide();
            } // ...and user has not done it
            else {
                $('.btn-adt').removeClass('btn-adt-active');
                $('.want-text, .adt-text').show();
                $('.unwant-text, .want-again-text, .done-it-text, .done-again-text').hide();
            }
        } // user wanted to do it once
        else if (userWTD.numTimesWtd == 1) {
            // ...and has not already done it
            if (userWTD.numTimesAdt == 0) {
                $('.btn-adt').removeClass('btn-adt-active');
                $('.want-text, .done-it-text').show();
                $('.want-again-text, .unwant-text, .adt-text, .done-again-text').hide();
            } // ...and has done it at least once
            else {
                $('.btn-adt').addClass('btn-adt-active');
                $('.want-again-text').show();
                $('.want-text, .unwant-text, .adt-text').hide();

                // user already did it once    
                if (userWTD.numTimesAdt == 1) {

                    $('.done-it-text').show();
                    $('.done-again-text').hide();
                } // user already did it > once
                else {
                    $('.done-again-text').show();
                    $('.done-it-text').hide();
                }
            }
        } // user wanted to do it > once
        else {
            $('.btn-adt').addClass('btn-adt-active');
            $('.want-again-text').show();
            $('.want-text, .unwant-text, .adt-text').hide();

            // ...and already did it once
            if (userWTD.numTimesWtd == 2 && userWTD.numTimesAdt == 1) {
                $('.done-it-text').show();
                $('.done-again-text').hide();
            } // ...and already did it > once
            else {
                $('.done-again-text').show();
                $('.done-it-text').hide();
            }
        }
    }
    else { // User wants to do something
        // Increment # times user wants to do it
        ++userWTD.numTimesWtd;

        // ADT button was previously clicked
        // i.e. this button is now 'Want to do it *again*'
        if ($('.btn-adt').hasClass('btn-adt-active')) {
            $(this).addClass('btn-wtd-active btn-info');
            $('.btn-adt').removeClass('btn-adt-active');

            // user has done it at least once
            if (userWTD.numTimesWtd > 0) {
                // user only wanted to do it once
                if (userWTD.numTimesWtd == 1) {
                    // user already did it once
                    if (userWTD.numTimesAdt > 0) {
                        $('.want-again-text, .done-again-text').show();
                        $('.want-text, .adt-text, .done-it-text').hide();
                    } // user already did it
                    else {
                        $('.want-again-text, .done-it-text').show();
                        $('.want-text, .adt-text, .done-again-text').hide();
                    }
                }
                else { // user wanted to do it more than once
                    $('.want-again-text, .done-again-text').show();
                    $('.want-text, .adt-text, .done-it-text').hide();
                }
            }
        }
        else { // ADT button was NOT previously clicked
            $(this).addClass('btn-wtd-active btn-info');

            // user wanted to do it once and has not already done it
            if (userWTD.numTimesWtd == 1 && userWTD.numTimesAdt == 0) {
                $('.want-text, .done-it-text').show();
                $('.want-again-text, .adt-text, .done-again-text').hide();
            } // user wanted to do it > once and has already done it
            else if (userWTD.numTimesWtd > 1 && userWTD.numTimesAdt > 0) {
                $('.want-again-text, .done-again-text').show();
                $('.want-text, .adt-text, .done-it-text').hide();
            }
        }
    }

    console.dir(userWTD);
});

// Update ADT (Already Did This) text when user clicks the button
$('.btn-adt').on('click', function() {

    // User wants to Undo their 'already done this'
    if ($(this).hasClass('btn-adt-active')) {

        // Adjust WTD object properties
        if (userWTD.numTimesAdt > 0)--userWTD.numTimesAdt;
        else userWTD.numTimesAdt = 0;

        $(this).removeClass('btn-adt-active');

        // user has not yet done it
        if (userWTD.numTimesAdt == 0) {
            // ...and user has wanted to do it once
            if (userWTD.numTimesWtd == 1) {
                $('.btn-wtd').addClass('btn-wtd-active btn-info');
                $('.want-text, .done-it-text').show();
                $('.want-again-text, .unwant-text, .adt-text, .done-again-text').hide();
            } // ...and user has not wanted to do it
            else {
                $('.btn-wtd').removeClass('btn-wtd-active btn-info');
                $('.want-text, .adt-text').show();
                $('.want-again-text, .unwant-text, .done-it-text, .done-again-text').hide();
            }
        } // user already did it once
        else if (userWTD.numTimesAdt == 1) {
            // ...and wanted to do it once
            if (userWTD.numTimesWtd >= 1) {
                $('.btn-wtd').addClass('btn-wtd-active btn-info');
                $('.want-again-text, .done-again-text').show();
                $('.want-text, .adt-text, .done-it-text').hide();
            }
        } // user already did it > once                    
        else if (userWTD.numTimesAdt > 1) {
            $('.btn-wtd').addClass('btn-wtd-active btn-info');
            $('.want-again-text, .done-again-text').show();
            $('.want-text, .unwant-text, .adt-text, .done-it-text').hide();
            // ...and wanted to do it at least once
        }
    } // User (already) did it
    else {

        // Increment # times user did it
        ++userWTD.numTimesAdt;
        userWTD.alreadyDidIt = true;

        $(this).addClass('btn-adt-active');
        if ($('.btn-wtd').hasClass('btn-wtd-active')) {
            $('.btn-wtd').removeClass('btn-wtd-active btn-info');
        }

        // User has done it at least once
        if (userWTD.numTimesAdt > 0) {
            // user did it exactly once
            if (userWTD.numTimesAdt == 1) {
                $('.want-again-text').show();
                $('.want-text, .done-again-text').hide();
                // user has not wanted to do it
                if (userWTD.numTimesWtd == 0) {
                    $('.adt-text').show();
                    $('.done-it-text').hide();
                } // user has wanted to do it once
                else if (userWTD.numTimesWtd == 1) {
                    $('.done-it-text').show();
                    $('.adt-text').hide();
                }
            }
            else { // user did it more than once
                $('.want-again-text, .done-again-text').show();
                $('.want-text, .adt-text, .done-it-text').hide();
            }
        }

        $(this).prop('title', 'Undo');
    }
    //$(this).toggleClass('btn-adt-active');
    console.dir(userWTD);
});

$('.btn-wtd').hover(function() {
    // user indicated they wtd something
    if ($(this).hasClass('btn-wtd-active')) {
        if ((userWTD.numTimesWtd > 0 && userWTD.numTimesAdt > 0) || (userWTD.numTimesAdt > 0 && userWTD.numTimesWtd >= 0)) $('.want-again-text').hide();
        else $('.want-text').hide();
        $('.unwant-text').show();
    }
}, function() {
    $('.unwant-text').hide();
    if ((userWTD.numTimesWtd > 0 && userWTD.numTimesAdt > 0) || (userWTD.numTimesAdt > 0 && userWTD.numTimesWtd >= 0)) $('.want-again-text').show();
    else $('.want-text').show();
})
\$\endgroup\$
  • \$\begingroup\$ The HTML and CSS seem fine at first glance, but the JS is indeed far too long and needs to be refactored and improved. \$\endgroup\$ – jsanc623 Oct 12 '12 at 17:05
  • \$\begingroup\$ Please always post your code inline in the question as stated in the faq. \$\endgroup\$ – Adam Oct 12 '12 at 17:14
  • \$\begingroup\$ Ok thanks, and I also added the code inline. \$\endgroup\$ – Ray Oct 12 '12 at 17:25
1
\$\begingroup\$

If we consider these two buttons a state machine, we see that there are six simple states (not including hover).

  1. "want" / "did"
  2. "want again" / "did" (active)
  3. "want" (active) / "done"
  4. "want again" / "done" (active)
  5. "want again" (active) / "done again"
  6. "want again" / "done again" (active)

The path is one of these two options. In the first path, done is clicked first. In the other, want is clicked first.

1--2-----5--6
1--3--4--5--6

Acknowledging the state machine, look at the "want"/"want again" button. The text will always say "I want to do it again" after the done button has clicked. Essentially we can boil it down to this:

$('.want-text').toggle(done <= 0)
$('.want-again-text').toggle(done > 0)

Moving on to the "did"/"done"/"done again" button is a little trickier. "I already did it" will be visible so long as the want count is 0, and "Done it again!" will be visible after the want count is 2 or greater, but "Done it!" is more complex since it only shows up on one of the state machine paths (step 3/4). It turns out it is visible when the want count is 1 and we did not click the done button first and the done count is 0 or 1. This all leads to this block of code:

    var didText = false, doneText = false, againText = false
    if(want <= 0) 
        didText = true
    else if (want === 1 && !doneFirst && done <= 1) 
        doneText = true
    else 
        againText = true            

    $('.adt-text').toggle(didText)
    $('.done-it-text').toggle(doneText)
    $('.done-again-text').toggle(againText)

Finally, we have to consider when to make each button active. If we clicked the done button first, then done will be active when the done count is greater than the want count. The opposite is true if the want button was clicked first. As a special case, no button is active if the want count and done count are zero.

    var wantActive, doneActive;
    if (want === 0 && done === 0){
        wantActive = doneActive = false
    } else {
        doneActive = (doneFirst ? done > want : done === want)
        wantActive = !doneActive
    }
    $('.btn-wtd').toggleClass('btn-wtd-active btn-info', wantActive)
    $('.btn-adt').toggleClass('btn-adt-active', doneActive)
        .prop('title', doneActive ? 'Undo' : '');

Once we have all of that, we can rip out the entirety of the click functions, replacing them a simple redraw function (containing the previous code blocks). Since we are now concerned about clicking the done button first, a little code is needed to set that variable.

One final note: the userWTD object was not needed, so I removed it and renamed the numTimesWtd/numTimesAdt variables.

Working Fiddle: http://jsfiddle.net/danthegoodman/nXseg/

$(function() {
    var want = 0
    var done = 0
    var doneFirst = false

    $('.btn-wtd').on('click', function() {
        if ( $(this).hasClass('btn-wtd-active') ){
            want -= 1;
            if (want <= 0) want = 0;
        } else {
            want += 1;
        }

        redraw();
    });

    $('.btn-adt').on('click', function() {
        if ( $(this).hasClass('btn-adt-active') ){
            done -= 1;
            if (done < 0) done = 0;
            if (want === 0 && done === 0) doneFirst = false;
        } else {
            if(want === 0 && done === 0) doneFirst = true;
            done += 1;
        }

        redraw();
    });

    $('.btn-wtd').hover(function() {
        // user indicated they wtd something
        if ($(this).hasClass('btn-wtd-active')) {
            $('.want-again-text').hide();
            $('.want-text').hide();
            $('.unwant-text').show();
        }
    }, function() {
        redrawWantText()
    })

    function redraw(){
        var wantActive, doneActive;

        if (want === 0 && done === 0){
            wantActive = doneActive = false
        } else {
            doneActive = (doneFirst ? done > want : done === want)
            wantActive = !doneActive
        }
        $('.btn-wtd').toggleClass('btn-wtd-active btn-info', wantActive)
        $('.btn-adt').toggleClass('btn-adt-active', doneActive)
            .prop('title', doneActive ? 'Undo' : '');

        redrawWantText()
        redrawDoneText()
    }

    function redrawWantText(){
        $('.unwant-text').hide();
        $('.want-text').toggle(done <= 0)
        $('.want-again-text').toggle(done > 0)
    }

    function redrawDoneText(){
        var didText = false, doneText = false, againText = false

        if(want <= 0) didText = true
        else if (want === 1 && !doneFirst && done <= 1) doneText = true
        else againText = true            

        $('.adt-text').toggle(didText)
        $('.done-it-text').toggle(doneText)
        $('.done-again-text').toggle(againText)
    }
});
\$\endgroup\$
  • \$\begingroup\$ wow, your insight on this was seriously great, some of the points you mentioned I kind of touched on a bit but I didn't go as deep as you did, thanks so much, this really opened up my mind to the problem some more. As a side note I think I may also add a counter text next to the 'want' button to provide a reminder of where you're at in terms of wanting/doing. \$\endgroup\$ – Ray Oct 15 '12 at 3:30

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.