As you may have already heard, we recently finished building our new mobile site! I’m here to talk a bit about how we built it and why we did the things we did.
- Backbone plays very nicely with Rails out of the box.
- We’re already using it in other places across the Wistia app.
All Backbone needs to get going is a RESTful JSON API, and Rails can fill that role quite nicely. We use Rails active model serializers to specify attributes that need to be passed along to the front end, and we created a special internal JSON API for our client-side apps to access. Voila, data.
So, we’ve got all the data we need. Great! Now, time to design! We started by defining our ideal Wistia mobile experience. It needed to feel friendly, snappy, and undeniably "Wistia." Among other qualities, that means looking nice, incorporating smooth transitions, and keeping the focus on the content.
Really though, we just want to make you smile.
The trickiest part of this whole design process was making the site feel like a real app. For example, mobile Safari waits 300ms by default when you click a link for it to actually navigate, in case you hit the link while you’re scrolling. Clicking should be intentional.
Many of the touch library options out there offered too much or were too invasive, so we ended up rolling our own.
class @Apatosaurus.Views.ApatosaurusView extends Backbone.View clickEventType: -> if 'ontouchstart' of window then 'touchstart' else 'click' afterRender: -> @_addTouchClass ?= => @addTouchClass(arguments...) @_removeTouchClass ?= => @removeTouchClass(arguments...) @$el.off @touchStartEvent(), '[data-touch-responsive]', @_addTouchClass @$el.off @touchEndEvent(), '[data-touch-responsive]', @_removeTouchClass @$el.on @touchStartEvent(), '[data-touch-responsive]', @_addTouchClass @$el.on @touchEndEvent(), '[data-touch-responsive]', @_removeTouchClass @$el.on @touchMoveEvent(), '[data-touch-responsive]', @_removeTouchClass @_preventDefault ?= (event) => event.preventDefault() @$el.off 'click', 'a', @_preventDefault @$el.on 'click', 'a', @_preventDefault @_markDrag ?= => @_dragging = true @_endDrag ?= => @_dragging = false @_interceptClick ?= => @interceptClick(arguments...) @$el.off @touchMoveEvent(), 'a', @_markDrag @$el.on @touchMoveEvent(), 'a', @_markDrag @$el.off @touchEndEvent(), 'a', @_interceptClick @$el.on @touchEndEvent(), 'a', @_interceptClick @$el.off @touchEndEvent(), 'a', @_endDrag @$el.on @touchEndEvent(), 'a', @_endDrag @$el.trigger 'ready' interceptClick: (event) -> unless @_dragging $elem = $(event.target or event.srcElement).closest('a') if $elem.attr('data-intercept-click') && $elem.attr('data-intercept-click') == 'true' Apatosaurus.router.navigate($elem.attr('href'), trigger: true) else window.location.href = $elem.attr('href') touchStartEvent: -> if `’ontouchstart’ in document.documentElement` 'touchstart' else 'mousedown' touchEndEvent: -> if `'ontouchstart' in document.documentElement` 'touchend' else 'mouseup' touchMoveEvent: -> if `'ontouchstart' in document.documentElement` 'touchmove' else 'mousemove' addTouchClass: (event) -> $elem = $(event.srcElement or event.target) .closest('[data-touch-responsive]') $elem.addClass('touch') removeTouchClass: (event) -> $elem = $(event.srcElement or event.target) .closest('[data-touch-responsive]') $elem.removeClass('touch')
We knew exactly what we needed, and didn’t want to overcomplicate things.
Wistia’s engineering team is growing. If someone comes to work on the mobile site, they should find the code pretty familiar and have an easy time getting acquainted with it. Using Backbone.js as a front end for our main Rails app meant that the front end mirrored the structure of the back end.
../ collections/ models/ routers/ utils/ views/ base.coffee uploader.coffee
It’s pretty easy to poke around in here and see how things work. The router listens for changes in the url, renders new pages, and handles page transitions. Models make the JSON we get from Rails accessible via instance methods. Collections are just, well, groups of models. Views are the unit of content delivery. We have views for different headers and pages. Many, many views.
We’re pretty happy with how our mobile site turned out. But really, we made this for you. We hope you love it too. What do you think?