Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Thursday, February 10, 2011

Rails-like number_with_delimiter in javascript

The number_with_delimiter view helper is Rails is nice. I wanted to do the same thing in Javascript. I went to just grab it from Rails and port it to js. But first I googled and found someone had already done exactly that. I took his code and made it extend Number.protoype so you can do something like:


var num = 12345678;
var str = num.number_with_delimiter();
alert(str);


This will show that str is "12,345,678".

Her's my version of the code:



Based on code from: http://kevinvaldek.com/number-with-delimiter-in-javascript

Saturday, April 17, 2010

How to detect what other sites your visitors have been to

When someone visits your site, do you want to know where else they've been? Maybe you want to know if they've been to a competitor or affiliate site so you can make them special offers. Maybe you want to intelligently suggest they signup for your site with Facebook Connect, Twitter, Google OpenID, or Yahoo! ID, based on which of those sites you know they frequent. Maybe you're just nosy.

Regardless, there are pretty good controls in place with most browser to prevent you from developing javascript on your site that can sniff the user's browser history. There's one small loophole though...you can stick all the URLs you want to know about in your page, and test to see if they have been visited, using the CSS selector features of javascript frameworks such as jQuery and Prototype.js. This is not a fool-proof way to tell where they've been, but it might just be good enough for the kinds of suggestions you want to make to your user.

Based on ideas from Spyjax (described in this blog post), I whipped up a simpler demonstration of this using Haml to make a hidden list of URLs and, instead of trying to sniff links by their colors, using Prototype.js to detect which of them have been visited. This could have just as easily been done in jQuery:



One extension to this technique, if you have lots of URLs to test, might be to dynamically load 100 at a time via AJAX periodically, and asynchronously trigger actions in your page when visited URLs are detected.

Isn't that a little creepy? What if I don't want sites to get this info about me?

The Spyjax blog post linked to earlier has a decent answer for this:

The less browser history you store the fewer URLs someone can steal from that history. In Firefox you can change the amount of browser history by going to Tools -> Options -> Privacy and then either uncheck the “Remember visited pages” checkbox or change the number of days that history is stored for.

Wednesday, January 27, 2010

Fixing IE onChange event for checkboxes and radio buttons

In Internet Explorer (up to at least IE8) clicking a radio button or checkbox to change its value does not actually trigger the onChange event until the the input loses focus. On every other browser, when the value of the input changes, onChange is triggered.

This is quite annoying, for example, if you want to trigger some other page changes off of the selection of a radio button in a radio button group changing. Some may suggest using onClick instead, and while that may be sufficient for a checkbox, for radio buttons, this is not a good solution. The problem is that onClick will fire when you click the radio button that is already selected, so you'll get your code triggered even when the value doesn't change.

Here's a quick hack that worked for me. On IE, handle the onClick event of radio and checkboxes and have it blur and re-focus the input element. This will trigger it to signal the onChange event like you'd expect from a decent browser. Then, you can go about your business using the onChange event like you should, and it will work correctly for you across the popular browsers. (WARNING: Of course, if you have other code triggered off of the blur and focus events, your mileage may vary.)

Here's an example of how you might do this using jQuery:


if ($.browser.msie) {
$(function() {
$('input:radio, input:checkbox').click(function() {
this.blur();
this.focus();
});
});
}

Sunday, November 30, 2008

Sharing a partial for both server-side and client-side HTML generation.

Say I have a page that shows a table of items (like a shopping cart), and I want to be able to generate the HTML for this table from the server-side (of course). Let's also say that I want to have client-side javascript be able to dynamically add items to this table without a round-trip to the server, based on data gathered on the client. What I end up with is some HTML code (actually, ERb or Haml) that defines the layout of a row in this table, and a duplicate piece of code in javascript that generates identical HTML. The server-side code uses a nice templating language like Haml to fill in the variable pieces, while the client-side code pieces together HTML embedded in strings concatenated with the dynamic data.

Not very DRY. Not easy to maintain. Not pretty to look at.

So...What's a good solution that allows me to write the HTML once, and have one place to maintain it, while still being able to use it to dynamically evaluate the template based on parameterized data on both the server and client sides?

Below is a solution that I came up with. (And I would love to hear any better ideas).

For brevity, I'll use a very simple HTML structure in these examples, but in practice I have used this with much more complex structure and logic. Here's what our example table looks like:


<table id="items">
<tr>
<td class="name">Shirt</td>
<td class="desc">White short-sleeved shirt</td>
<td class="price">$5.99</td>
</tr>

<tr>
<td class="name">Pants</td>
<td class="desc">Blue pants with zipper</td>
<td class="price">$25.99</td>
</tr>
</table>


Assume we have this built using Haml, and there are two partials: one for the top-level table, and one for an individual row. E.g.:

items_table.haml:

%table#items
- items.each do |item|
= render :partial => 'item_row', :locals => {:item => item}


item_row.haml:

%tr
%td.name= item.name
%td.desc= item.desc
%td.price= item.price


In the client-side javascript in our top-level Haml partial, assume we have some function that gets called from some user input action (like filling out a form and clicking a button). E.g.:


:javascript
var addItem = function(name, desc, price) {
var html =
"<tr>" +
" <td class=\"name\">" + name + "</td>" +
" <td class=\"desc\">" + desc + "</td>" +
" <td class=\"price\">" + price + "</td>" +
"</tr>";
$('items').insert(html); // using prototype.js
};


OK...so this doesn't seem so bad with such a simple example, but imagine that the HTML structure for a table row is something much more complex, with conditionals based on the item, and maybe even usage of additional sub-partials. This maintenance for this becomes a nightmare.

This first thing I am going to do is change the item_rows.haml partial. Instead of having it actually render the item's values, it's going to render template replacement variables using #{...} syntax. Note, these are NOT interpolated in the partial. We'll do that in a separate phase from rendering:

item_row.haml:

%tr
%td.name #{item_name}
%td.desc #{item_desc}
%td.price #{item_price}


Next, let's make a couple helper methods. The first one returns this partial rendered as a string. (I do this because in the real world, this takes a number of parameters, and adding a helper layer makes things easier to manage than always calling render directly). The second one renders this partial and then interpolates the #{...} strings inside it, based on the properties of the item we want to render -- effectively making this equivalent to rendering the original version of this template.

item_helper.rb:

module ItemHelper
# Make ActionController::Base#render_to_string available.
helper_method :render_to_string

def item_row_template
render_to_string :partial => item_row
end

def item_row(item)
# Get the template.
t = item_row_template

# Define the template replacement variables.
item_name = item.name
item_desc = item.desc
item_price = item.price

# Evaluate the template
eval(t.inspect.gsub("\\#", "#"))
end
end


This, of course, means we change the top-level partial to use this new item_row helper, like so:

items_table.haml:

%table#items
- items.each do |item|
= item_row(item)


At this point, we've got a functionally equivalent server-side. But the big benefit is that now the HTML template returned by the item_row_template helper method can also be used to do the same substitutions on the client side, thanks to the handy Template class in prototype.js. The Haml that defines the javascript addItem method now looks like this:


:javascript
var addItem = function(name, desc, price) {
var html = new Template(
'#{escape_javascript(item_row_template)}'
).evaluate({
item_name : name,
item_desc : desc,
item_price : price
});
$('items').insert(html);
};


Now, when the markup for the item row changes, it only needs to change in one place, and both the server-side and client-side benefit from it. This certainly feels more DRY...but also seems like a hefty price to pay in terms of complexity...