3
\$\begingroup\$

I've taken a stab at making a context menu using BackboneJS.

Here's the overall structure:

Collections:

  • /collection/contextMenuGroups
  • /collection/contextMenuItems

Models:

  • /model/contextMenu
  • /model/contextMenuGroup
  • /model/contextMenuItem

Views:

  • /view/contextMenuView

To start off, lets take a look at my template:

<script type="text/template" id="contextMenuTemplate">
 <% groups.each(function(group){ %>
 <ul id="group_<%= group.cid %>">
 <% group.get('items').each(function(item){ %>
 <li>
 <a id="groupItem_<%= item.cid %>" href="#"><%= item.get('text') %></a>
 </li>
 <% }); %>
 </ul>
 <% }); %>
</script>

My template is created such that each contextMenuGroup provided to it creates a new unordered list. Each item in each group creates a link inside of a listItem for the given unordered list.

Here's my view:

define(['contextMenu'], function (ContextMenu) {
 'use strict';
 var ContextMenuView = Backbone.View.extend({
 className: 'contextMenu',
 template: _.template($('#contextMenuTemplate').html()),
 parentSelector: 'body',
 render: function () {
 this.$el.html(this.template(this.model.toJSON()));
 // Prevent display outside viewport.
 var offsetTop = this.top;
 var needsVerticalFlip = offsetTop + this.$el.height() > $(this.parentSelector).height();
 if (needsVerticalFlip) {
 offsetTop = offsetTop - this.$el.height();
 }
 var offsetLeft = this.left;
 var needsHorizontalFlip = offsetLeft + this.$el.width() > $(this.parentSelector).width();
 if (needsHorizontalFlip) {
 offsetLeft = offsetLeft - this.$el.width();
 }
 // Show the element before setting offset to ensure correct positioning.
 this.$el.show().offset({
 top: offsetTop,
 left: offsetLeft
 });
 return this;
 },
 initialize: function () {
 // TODO: If I implement Backbone View's more properly, then 'body' should be responsible for this, but for now this is fine.
 this.$el.appendTo(this.parentSelector);
 var self = this;
 // Hide the context menu whenever any click occurs not just when selecting an item.
 $(this.parentSelector).on('click contextmenu', function () {
 self.$el.hide();
 });
 },
 show: function (options) {
 if (options.top === undefined || options.left === undefined) throw "ContextMenu must be shown with top/left coordinates.";
 if (options.groups === undefined) throw "ContextMenu needs ContextMenuGroups to be shown.";
 this.top = options.top;
 this.left = options.left;
 this.model = new ContextMenu({
 groups: options.groups
 });
 this.render();
 }
 });
 return ContextMenuView;
});

To display a contextMenu, I call show and pass to show top/left coordinates as well as a model to render inside of the context menu.

Usage:

showContextMenu: function (event, queueItem) {
 var queueContextMenuGroups = [{
 position: 0,
 items: [{
 position: 0,
 text: 'Clear Queue'
 }]
 }];
 if (queueItem) {
 queueContextMenuGroups.push({
 position: 1,
 items: [{
 position: 0,
 text: 'Remove ' + queueItem.get('title')
 }]
 });
 }
 this.contextMenuView.show({
 top: event.pageY,
 left: event.pageX + 1,
 groups: queueContextMenuGroups
 });
 return false;
}

Here, I create an anonymous array of objects (groups) which contains some items to render.

This all works great, but I have no idea how to bind generic events! Should I be extending my ContextMenuView with a 'specific' implementation which can define its events?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Jul 17, 2013 at 16:45
\$\endgroup\$
0

1 Answer 1

3
\$\begingroup\$

I like the code as is,

  • Use of 'use strict'
  • JsHint cannot find anything
  • Readable, well named variables
  • No obvious copy pasting or repeated code

As for events, I would

  • Wait until everything is rendered
  • Add a $.click() to each menu item, item by selecting on a classname used only by those menu items, or you could re-build the id's of each menu item and attach to each one individually ( first approach would be much better ).
  • Then I would use $.trigger() to send out an event with the text of the button
  • Register whoever needs to know that a menu item was clicked for that event
answered Apr 28, 2014 at 14:57
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.