Pull to Refresh

Pull to refresh is a common design pattern amongst major mobile apps. Facebook, twitter, gmail, etc all use a “pull to refresh” technique. In my previous post on the new Quick Return design pattern, I discussed a way to implement this in a desktop environment. Now, I present to you, how to implement the “Pull to Refresh” design pattern on the desktop:

Demo

Usage:

Simply download the JS source or copy/paste the JS source below and include it in your markup.

A global, called Refreshable, becomes available once the script is included. To use, you can call its initialize(), which takes in 2 options as an object{}:

  1. refreshAction
  2. refreshContainer

refreshAction must be a function. It is the action you want to bind to the refresh event triggered on the refreshContainer.

refreshContainer needs to be addressed by an ID. The script, at this time, doesn’t support an array of elements.

So, in the source below you’ll see where I initialized it saying:

Refreshable.initialize({
refreshAction: function() {
$.getJSON("http://date.jsontest.com/", function(data) {
$("#container p").html("Today is " + data.date + " and it is " + data.time);
});
},
refreshContainer: "#container"
});

I’m passing in a function that goes and fetches the current date and time from a provider, then updates the container w/ this information.

HTML:

<div id="container" class="container">
<p>Pull my bottom</p>
</div>

CSS:

.container { 
width: 350px;
height: 350px;
padding: 5px;
margin: 5px;
background: #ccc;
position: relative;
border-bottom-right-radius: 10px;
}

JS:

var Refreshable = (function () {
var refreshAction;
var refreshContainer;
var $refreshContainer;
var refreshContainerHeight;
var refreshElString;
var $refreshEl;
var offset;
var offsetDiff; startHover = function (evt) {
$(this).css("background-color", "rgba(0,0,0,0.5)");
}
stopHover = function (evt) {
$(this).css("background-color", "transparent");
}
allowResize = function (evt) {
evt.preventDefault();
$refreshContainer.addClass("resizable");
offset = evt.pageY;
}
stopResize = function (evt) {
$refreshContainer.removeClass("resizable");
$refreshContainer.animate({
height: refreshContainerHeight
});
offset = 0;
}
resize = function (evt) {
if ($refreshContainer.hasClass("resizable")) {
offsetDiff = evt.pageY - offset;
if (offsetDiff > 0) {
$refreshContainer.innerHeight(refreshContainerHeight + offsetDiff);
}
if (offsetDiff > refreshContainerHeight * 0.25) {
$refreshContainer.trigger("refresh");
}
}
}
var refreshIt = function () {
stopResize();
refreshAction();
} return {
initialize: function (options) {
refreshAction = options.refreshAction;
refreshContainer = options.refreshContainer;
$refreshContainer = $(options.refreshContainer);
refreshContainerHeight = $(refreshContainer).innerHeight();
refreshElString = '<div class="refreshEl" style="position:absolute; width: 100%; height: 45px; bottom: 0; left: 0; cursor: s-resize; outline: 50px transparent solid; z-index: 999999; border-bottom-right-radius: 10px;"></div>';
$refreshContainer.append(refreshElString);
$refreshEl = $refreshContainer.children(".refreshEl").first();

this.eventRegister();
return this;
}, eventRegister: function () {
$refreshContainer.on("refresh", refreshIt);
$refreshEl.on("mousedown", allowResize)
.on("mouseup", stopResize)
.on("mousemove", resize)
.on("mouseleave", stopHover)
.on("mouseover", startHover); }
}; })();
//Code you saw before Refreshable.initialize({
refreshAction: function() {
$.getJSON("http://date.jsontest.com/", function(data) {
$("#container p").html("Today is " + data.date + " and it is " + data.time);
});
},
refreshContainer: "#container"
});

I wanted to keep the CSS down to a minimum for portability. The CSS included is simply for the container to serve as a demo, it doesn’t have to be followed strictly.

The only requirement of the CSS is that it needs to be positioned relatively. Otherwise, the refresh element appended to the bottom will not seat inside the container.

That’s all I have for now. As with my other posts, I’ll be making edits to make the code better, more extendable, etc.

Thanks for reading.

 

Advertisements

Sanitize Backbone.js Models to prevent XSS

This isn’t groundbreaking code, but you may find it useful to know how to take a step to prevent XSS attacks when implementing Backbone MV*. Some JavaScript frameworks automatically escape or sanitize the values being passed to the model. Backbone does not. However, the library it’s built on (Underscore.js) does provide an easy way to escape values.

First, in your Model, create your initialize() method, and in it, use the _.each method to iterate over the attributes object, passing the value to a method belonging to the Model named “sanitize”.

var CartItem = Backbone.Model.extend({
  initialize: function() {
    _.each(this.attributes, function (val, key)
      this.set(key, this.sanitize(val));
    }, this);
  }
});

Let’s add our sanitize() method. In this method, simply take in the string as a parameter, and return the escaped version of it, using Underscore’s _.escape method.

var CartItem = Backbone.Model.extend({
  initialize: function() {
    _.each(this.attributes, function (val, key) {
      this.set(key, this.sanitize(val));
    }, this);
  },
  sanitize: function (str) { 
    return _.escape(str) 
  } 

});

I recommend sanitizing in your model’s initializer so that you ensure it’s properly scrubbed before calling Model.save() and sending nasty XSS or SQL injection to your server. Also, sanitize anywhere you update the model or allow edits to the model.

I hope you found this post useful. If you have anything to add or find any problems, let me know.