Is this your first visit? You may want to subscribe to the feed.

Articles tagged with javascript

Showing Ajax activity with CSS

You know when you have a form that submits with Ajax, and you want to show some type of activity on the form? There’s several ways to do this, like including a hidden image that you show and hide while there is activity, or inserting and removing an image with javascript. These all just feel too dirty to me. I don’t want to have to change the markup just because the form is submitting via Ajax.

As usual, there’s a cleaner way to do it with just CSS. I’ve used this simple but effective trick on a couple projects now, so I thought I would share.

First, here is a demo of what it looks like: (sorry, you’ll have to leave your feed reader.)

Click the save button.

or Cancel

This technique is very simple. It adds a class on the form with Javascript while it is loading, and then removes it once it’s done. The loading class hides the submit button and replaces it with a background image, usually an animated gif.

First, with Javascript, add a class to the form while the Ajax request is loading. It would look something like this in Prototype:

$('my_form').observe('submit', function(event) {
  event.stop();
  this.request({
    evalScripts: true,
    onLoading:  function() { this.addClassName('loading'); }.bind(this),
    onComplete: function() { this.removeClassName('loading'); }.bind(this)
  });
});

Next, wrap an element around your buttons or whatever you want to hide.

<form>
  <div class="buttons">…</div>
</form>

Lastly, there’s just a little CSS to hide buttons and any elements inside them, and set the background image:

form .buttons { text-align: center; }
form.loading .buttons {
  text-indent: -2000px;
  overflow: hidden;
  background: url(../images/loading.gif) no-repeat center center;
}
form.loading .buttons * { visibility: hidden; }

There you have it. It’s really simple, but it’s worked well.

Code: javascript Mar 04, 2009 ● updated Mar 03, 2009 1 comment

It's a search party!

While chatting with Dr. John Nunemaker at RubyConf, I realized that I have several problems. Ignoring the many character flaws that are beyond the scope of this post, my problems are:

  1. I tag things I find useful on Delicious. But I rarely look back to delicious because it’s just easier to search Google, resorting to Delicious if I can’t find something that I remember tagging.
  2. It’s easier to search Google because not everything that I find useful is in Delicious. I don’t want to have to think about where useful things are, I just want to search for them.

I want a search engine that prioritizes things that I’ve found useful in the past. Ideally, google would let me tag things and take that into account when calculating page rank. But, in the mean time…

Let’s have a search party!

I threw together a simple search interface that pulls in results from Google, Delicious, and just for fun, GitHub. It shows primarily Google results, but then in the sidebar, it shows results from Delicious, with my bookmarks highlighted at the top, and also results from GitHub. Check it out.

And if you use Firefox, you can add it as your search provider:

It is an extremely simple app that has 2 pages and uses JavaScript to read in JSON from all of the services. There is one Ruby class that does screen-scraping since Delicious doesn’t provide an API to their full search.

I first implemented it in Merb, but upon realizing how simplistic it is, I switched it over to Sinatra. And I used jQuery to do the JSON and other JavaScripty goodness. Thanks to Mark Van Holstyn for helping implement it.

The code is available on GitHub, so check it out, fork it, and make it more awesome.

I will be making a few other posts about specific things I learned when building this, including deploying sinatra apps and using jQuery to do JSON with Merb and Sinatra.

Code: javascript Nov 08, 2008 ● updated Nov 09, 2008 2 comments

Ajax and Request Forgery Protection

Rails 2.1 added protection for cross-site request forgery by embedding a session-based token in generated forms. Rails will not process a POSTed request without the token. For the most part, this protection is transparent. But occasionally, an Ajax request request gets left out in the cold without a token.

If your Ajax request is tied to a form on the page, then all is good because the form already has the authenticity token in it. It only happens when your Ajax request is not tied to a form but makes a POST request, which is a rare but occasionally useful.

So how do we let those Ajax requests in on the fun? We came up with the ingenious idea of just embedding the authenticity token in a meta tag on every page, which can then be used in the Javascript.

<meta name="authenticity-token" id="authenticity-token" content="<%= form_authenticity_token %>" />

The authenticity token is unique for each visitor, and already included in other parts of the page, so this doesn’t defeat the purpose of the request forgery protection.

We usually add a couple convenience methods for accessing the token in Javascript.

var Application = {
  authenticityToken: function() {
    return $('authenticity-token').content;
  },

  authenticityTokenParameter: function(){
   return 'authenticity_token=' + encodeURIComponent(Application.authenticityToken());
  }
}

Now, we have easy access to it whenever we need it.

new Ajax.Request(url, {
  parameters: Application.authenticityTokenParameter()
});
Code: javascript Oct 24, 2008 ● updated Oct 24, 2008 3 comments

The importance of var in JavaScript

I would consider myself proficient at JavaScript. Not a rockstar, but I can hold my own. But I didn’t learn it out of a book; I picked it up slowly over several years. So occasionally I come across something that I probably would have learned in the first couple chapters of a decent JavaScript book.

Today, I experienced the importance of declaring variables with var.

For the unenlightened like myself, JavaScript basically has two scopes for variables: global and local. Variables assigned outside of a function are global, and variables assigned inside of a function, using the var keyword, are local (not rocket surgery). However, if you leave the var keyword off, it assigns a global variable, regardless of where it’s declared.

Here’s a bit of code that demonstrates this:

function fail() {
  date = new Date();
  console.log('Before: ' + Number(date));
  setTimeout(function() {
    console.log('After: ' + Number(date));
  }, 1000);
}

fail();
fail();

The code defines a function that sets a variable, logs the variable, then defines a callback that logs the variable again on second later. If you call the function twice in a row, you can see the effect:

Before: 1220487263486
Before: 1220487263499
After: 1220487263499 <- This should be 1220487263486
After: 1220487263499

Declaring the date variable with var gives us this output:

Before: 1220487287985
Before: 1220487287994
After: 1220487287985 <- see, this is the same as the first line
After: 1220487287994

So remember kids, declare local variables with var!

Code: javascript Sep 03, 2008 ● updated Oct 14, 2008 5 comments

Observers for Ajax callbacks

In my Javascript-fu adventures over the past week or so, I’ve consistently run into the same problem. I want to do slight variations of an Ajax request on an individual basis. Here is a common method from my code: (Note: these examples use Event.addBehavior from Dan Webb’s excellent lowpro library for registering events.)

Event.addBehavior({
  // hijack any forms with the class "new" and submit them using Ajax
  'form.new:submit': function(event) {
    this.request({evalScripts: true,
      onLoading: function() { this.disable(); }.bind(this),
      onComlete: function() { this.enable(); }.bind(this)
    });
    Event.stop(event);
  }
});

But occasionally, I want a certain form to do something slightly different. For example, I have some forms that are hidden by default and I want to hide them again after they are submitted:

Event.addBehavior({
  'form.new.hidden:submit': function(event) {
    this.request({evalScripts: true,
      onLoading: function() { this.disable(); }.bind(this),
      onComlete: function() { this.enable(); }.bind(this),
      onSuccess: function() { new Effect.BlindUp(this); }.bind(this)
    });
    Event.stop(event);
  },
})

The problems with this are, 1) there is a lot of duplication, and 2) if this form has the “new” class name, it’s going to end up with 2 event handlers registered, both making an Ajax request.

So what I want is event observers on Form.request and Anchor.request for the Ajax request lifecycle, so I can call form.request.observe('loading', function() { … }), and that will be invoked any time an Ajax request is made for that form:

Event.addBehavior({
  // submit form requests using ajax
  'form.new:submit': function(event) {
    this.request({evalScripts: true});
    Event.stop(event);
  },

  // register observers to disable and enable the form
  'form.new': function() {
    this.request.observe({
      'loading': function() { this.disable(); }.bind(this),
      'complete': function() { this.enable(); }.bind(this)
    });
  },

  // register an observer to hide the form on success
  'form.new.hidden': function() {
    this.request.observe('success', function() {
      new Effect.BlindUp(this);
    }.bind(this));
  }
})

And this is where I need your help.

The first problem is that we need some type of event notification framework. I have that part solved by using a slightly modified version of Ryan Johnson’s Object.Event library, which basically allows you to add event observers to any object, and then call them by calling notify('eventname') within the object.

Next, I’ve made a modified version of Anchor.reqeust method from my post yesterday, which calls notify on each of the Ajax callbacks, and extends the request methods with the event notification methods:

if (!window.Anchor) var Anchor = new Object();

Anchor.Methods = {
  request: function(anchor, options) {
    anchor = $(anchor)
    callbacks = {}
    $A(['loading', 'complete', 'exception', 'failure', 'success']).each(function(event) {
      callbacks['on' + event.capitalize()] = function() {
        anchor.notify.apply(anchor, arguments.unshift(event));
      };
    });
    options = Object.extend(Object.extend({
      method: 'get'
    }, callbacks), options || {});
    return new Ajax.Request(anchor.readAttribute('href'), options);
  }
}
Object.Event.extend(Anchor.Methods.request);
Element.addMethods('a', Anchor.Methods);

This is where the second problem comes in. While the event notification methods are added to Anchor.Methods.request, they don’t get added to the anchor objects when the request method does. They’re getting lost in Prototype’s Element.extend method that adds the extensions to each element.

And as soon as I get that problem solved, I’m going to have another one: one instance of Anchor.request will be shared amongst all the anchor objects, so registering an observer on one will register it for every Ajax request. What I need then is for the event observer to keep track of the registered observers in each object, even though the methods are on the shared register function.

I could move the event registration methods to the anchor instead of on the request method, but that has problems of it’s own. Namely, each element already has an observe method for the browser’s events, and I can’t come up with a better name than observe. Besides, I think the methods belong on the request object; they are specific to the Ajax request.

So, does anyone have any good ideas? Is this a lame/unnecessary feature?

Code: javascript Jul 12, 2007 ● updated Jul 13, 2007 7 comments

Adding DOM methods with Prototype

I just discovered that prototype has a really cool way to add methods to all instances of any tag. Simply call Element.addMethods(tagname, methods). For example, here’s how I added a request method to anchors, which will just make an ajax call for the anchor’s href:

if (!window.Anchor) { var Anchor = new Object(); }

Anchor.Methods = {
  request: function(anchor, options) {
    anchor = $(anchor)
    options = Object.extend({method: 'get'}, options || {});
    return new Ajax.Request(anchor.readAttribute('href'), options);
  }
}
Element.addMethods('a', Anchor.Methods);

So now I can go through and hijack links with a specific tag name and just call request() on them:

$$('a.jax').each(function (link) {
  link.observe('click', function(event) {
    link.request({evalScripts: true});
    Event.stop(event);
  });
})
Code: javascript Jul 11, 2007 ● updated Jul 11, 2007 0 comments

Making cookies with Javascript

new Cookie({eggs: 1, flour: 3, sugar: 1.5, brownSugar: 1});

Oh, wait…not those kind of cookies (mmm, now I’m hungry for cookies).

The script.aculo.us wiki has some code for working with cookies in JavaScript. I’ve extended it a bit to allow for other options when setting the cookies. Here’s how to use it:

// setting cookies
Cookie.set('name', 'value');

// change domain, path, and expiration in # of days
Cookie.set('name', 'value', {
  domain: 'foobar.com',
  path: '/path',
  expires: 14
});

// the google cookie (doesn't expire)
Cookie.set('name', 'google', {expires: false});

// reading cookies
Cookie.get('name');

// Get an array all cookies that are set
Cookie.all();

// erase a cookie
Cookie.erase('name');

// check if browser accepts cookies
if(Cookie.accept()) {
  // do stuff with cookies
}

You can grab the code from here.

Code: javascript Jul 11, 2007 ● updated Jul 11, 2007 1 comment

Firebug lite for all those other browsers

If for some reason you use one of those other browsers, then you don’t have the pleasure of using Firebug for debugging (Safari nuts, I know you secretly debug your javascript and stylesheets with Firebug in Firefox, despite it “not looking like a Mac app”).

Fortunately for you, there’s Firebug Lite. From the website:

Firebug is an extension for Firefox, but what happens when you need to test your pages in Internet Explorer, Opera, and Safari? If you are using console.log() to write to Firebug’s console, you’ll wind up with JavaScript errors in these other browsers, and that’s no fun.

The solution is Firebug Lite, a JavaScript file you can insert into your pages to simulate the Firebug console in browsers that are not named “Firefox”.

To ease the pain of debugging Javascript in those other browsers, I’ve thrown together a little plugin that automagically includes Firebug Lite into your application in development mode. All you need to do is install the plugin and include the default javascripts in your layout (which you’re probably already doing):

<%= javascript_include_tag :defaults %>

Just hit F12 (Ctrl+F12 on Mac) to open Firebug Lite on any page, or jump the focus directly to the command line with Ctrl+Shift+L (or ⌘+Shift+L on Mac).

http://source.collectiveidea.com/public/rails/plugins/firebug
Code: javascript Jul 02, 2007 ● updated Jul 02, 2007 0 comments

Subscribe

Browse by Tag