The usual way I approach the problem is by asking: what does change?
self.atFront
is flipped: not an issue here, you can simply writeself.atFront = !self.atFront
.- conditions using
atFront
vs.!atFront
(not an issue: if we factorize everything, we don't need to know what the state is: we just want to change it) - the string in
self.trigger
$front
vs.$back
removeClass()
vs.addClass()
.
Quite a lot of things change, actually! At this point, we don't know yet if avoiding duplication is worth it: it often leads to more complicated code and it can be longer, too. But let's try it. I took your code, created a flip
function, and tried to see what could fit in it. When writing this, I realized that it was simpler to put everything in the flip
function, so I tried it:
var TwistPanel = Backbone.View.extend({
initialize: function() {
this.$front = this.$el.find('.twist-panel-side-front');
this.$back = this.$el.find('.twist-panel-side-back');
this.atFront = true;
this.isFlipping = false;
// Fix the height of the twist panel container.
this.$el.height(this.$front.outerHeight(true));
},
flip: function() {
var self = this;
var otherSide = ... ? this.$back : this.$front;
if (this.isFlipping) return;
this.$el.height(otherSide.outerHeight(true));
this.$front.afterTransition(function() {
self.isFlipping = false;
self.trigger('twist', otherSide.name);
});
this.isFlipping = true;
if (this.$el.hasClass('twist')) {
this.$el.removeClass('twist')) {
} else {
this.$el.addClass('twist');
}
}
});
(I don't know about your application, but I guess toFront
can never be called when we're already in front. If it's not true, then we need to keep toFront
and toBack
which will call flip
themselves.)
At this point, I have two big issues left: I don't know how to decide what is the other side, and I don't know how to get its name (otherSide.name
, used in the self.trigger()
call). Since I don't know about this, I decided to resolve another issue, even if it is minor. This issue is that the removeClass/addClass
logic looks wrong. It would probably be simpler to use something that is more than a value, and HTML5 data-* attributes are perfect for this since they are key-value and supported by jQuery. You can then replace the last five lines by this single one:
this.$el.data('twist', otherSide.name);
$el
will either contain data-twist="front"
or data-twist="back"
: you then need to change your CSS to account for this minor change. What's nice here is that it's now possible to get the string you're interested in (front
or back
). And it's also easy to assign otherSide
. We'll use jQuery data()
for this. Here's the final untested code:
var TwistPanel = Backbone.View.extend({
initialize: function() {
this.$front = this.$el.find('.twist-panel-side-front');
this.$back = this.$el.find('.twist-panel-side-back');
this.isFlipping = false;
// Fix the height of the twist panel container.
this.$el.height(this.$front.outerHeight(true));
},
flip: function() {
var self = this;
var otherSide = this.$el.data('twist') === 'front' ? this.$back : this.$front;
var otherSideName = this.$el.data('twist') === 'front' ? 'back' : 'front';
if (this.isFlipping) return;
this.$el.height(otherSide.outerHeight(true));
this.$front.afterTransition(function() {
self.isFlipping = false;
self.trigger('twist', this.$el.data('twist'));
});
this.isFlipping = true;
this.$el.data('twist', otherSideName);
}
});