# Reproducing Google Schemer buttons effect

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,
wantsToDoIt: 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')) {

$(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
// user did it exactly once
$('.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();
})

• The HTML and CSS seem fine at first glance, but the JS is indeed far too long and needs to be refactored and improved. – jsanc623 Oct 12 '12 at 17:05
• Please always post your code inline in the question as stated in the faq. – Adam Oct 12 '12 at 17:14
• Ok thanks, and I also added the code inline. – Ray Oct 12 '12 at 17:25

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)
}
});

• 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. – Ray Oct 15 '12 at 3:30