I'm learning BackboneJS and I just made an attempt at converting a pre-existing module to a Backbone.View. I was hoping to get some feedback on my attempt and learn. I've been using the annotated ToDo source as a guide.
Here's some HTML to give you a rough idea:
<div id="VolumeControl">
<div id="MuteButton" class="volumeControl" title="Toggle Volume">
<svg width="16" height="16">
<path d="M0,6 L3,6 L7,2 L7,14 L3,10 L0,10Z" fill="#fff" />
<rect class="MuteButtonBar" id="MuteButtonBar1" x="9" y="6.5" width="1" height="3" />
<rect class="MuteButtonBar"id="MuteButtonBar2" x="11" y="5" width="1" height="6" />
<rect class="MuteButtonBar" id="MuteButtonBar3" x="13" y="3.5" width="1" height="9" />
<rect class="MuteButtonBar" id="MuteButtonBar4" x="15" y="2" width="1" height="12" />
</svg>
</div>
<div id="VolumeSliderWrapper" class="volumeControl">
<input type="range" id="VolumeSlider" class="volumeControl" title="Click or drag to change the volume." min="0" max="100" step="1" value="0" />
</div>
</div>
It's essentially a two-part control consisting of a mute button and an HTML5 range slider which expands out.
Here's a quick screenshot to bring things together mentally:
Here's my Backbone.View:
// Responsible for controlling the volume indicator of the UI.
define(['player'], function (player) {
'use strict';
var volumeControlView = Backbone.View.extend({
el: $('#VolumeControl'),
events: {
'change #VolumeSlider': 'setVolume',
'click #MuteButton': 'toggleMute',
'mousewheel .volumeControl': 'scrollVolume',
'mouseenter .volumeControl': 'expand',
'mouseleave': 'contract'
},
render: function () {
var volume = player.get('volume');
// Repaint the amount of white filled in the bar showing the distance the grabber has been dragged.
var backgroundImage = '-webkit-gradient(linear,left top, right top, from(#ccc), color-stop(' + volume / 100 + ',#ccc), color-stop(' + volume / 100 + ',rgba(0,0,0,0)), to(rgba(0,0,0,0)))';
this.volumeSlider.css('background-image', backgroundImage);
var activeBars = Math.ceil((volume / 25));
this.muteButton.find('.MuteButtonBar:lt(' + (activeBars + 1) + ')').css('fill', '#fff');
this.muteButton.find('.MuteButtonBar:gt(' + activeBars + ')').css('fill', '#666');
if (activeBars === 0) {
this.muteButton.find('.MuteButtonBar').css('fill', '#666');
}
var isMuted = player.get('muted');
if (isMuted) {
this.muteButton
.addClass('muted')
.attr('title', 'Click to unmute.');
} else {
this.muteButton
.removeClass('muted')
.attr('title', 'Click to mute.');
}
return this;
},
// Initialize player's volume and muted state to last known information or 100 / unmuted.
initialize: function () {
this.volumeSliderWrapper = this.$('#VolumeSliderWrapper');
this.volumeSlider = this.$('#VolumeSlider');
this.muteButton = this.$('#MuteButton');
// Set the initial volume of the control based on what the YouTube player says is the current volume.
var volume = player.get('volume');
this.volumeSlider.val(volume).trigger('change');
this.listenTo(player, 'change:muted', this.render);
this.render();
},
// Whenever the volume slider is interacted with by the user, change the volume to reflect.
setVolume: function () {
var newVolume = parseInt(this.volumeSlider.val(), 10);
player.set('volume', newVolume);
this.render();
},
// Adjust volume when user scrolls mousewheel while hovering over volumeControl.
scrollVolume: function (event, delta) {
// Convert current value from string to int, then go an arbitrary, feel-good amount of volume points in a given direction (thus *3 on delta).
var newVolume = parseInt(this.volumeSlider.val(), 10) + delta * 3;
this.volumeSlider.val(newVolume).trigger('change');
},
toggleMute: function () {
var isMuted = player.get('muted');
player.set('muted', !isMuted);
},
// Show the volume slider control by expanding its wrapper whenever any of the volume controls are hovered.
expand: function () {
this.volumeSliderWrapper.addClass('expanded');
},
contract: function () {
this.volumeSliderWrapper.removeClass('expanded');
}
});
var volumeControl = new volumeControlView;
})
Am I doing too much in render? Anything look weird?
player
as a dependency of thevolumeControlView
class rather than a parameter to the constructor? I'm not saying this is wrong, I've just never seen this. I've always seen the module define a class and export/return the class for other code to instantiate, passing in the appropriate parameters (e.g.player
). \$\endgroup\$