Prototype: "element-id".$() instead of $('element-id')

9 views
Skip to first unread message

Dr Nic

unread,
Sep 11, 2006, 3:59:26 AM9/11/06
to Ruby on Rails: Spinoffs
The Prototype library gives us the $() operation for converting a DOM
element id into the DOM element: $('element-id'). It also appends a
bunch of functions to the resulting object.

Sometimes though, passing a string into the $() function doesn't read
well; and only makes Javascript code harder to read. For example:
$(window.button_list()[3]).hide()

Instead, it'd be nice to have normal chainability. I like the
following syntax: window.button_list()[3].$().hide().

That is, call the $() method on a string object, instead of passing the
string into the $() method.

Add this code into your application:

String.prototype.$ = function() {
return $(document.getElementById(this));
}

>From article:
http://drnicwilliams.com/2006/09/11/prototype-call-dollar-on-string/

Christophe Porteneuve aka TDD

unread,
Sep 11, 2006, 5:51:48 AM9/11/06
to rubyonrail...@googlegroups.com
Hi,

Dr Nic a écrit :


> Sometimes though, passing a string into the $() function doesn't read
> well; and only makes Javascript code harder to read. For example:
> $(window.button_list()[3]).hide()

Actually, this is widely acclaimed as a nice way to do it. Of course,
there's no accounting for taste, but it is nicely unobstrusive, which is
the whole point of choosing such a short name.

> Instead, it'd be nice to have normal chainability. I like the
> following syntax: window.button_list()[3].$().hide().

Suit your taste, it's pretty easy to do so. But I don't think I dare
too much in asserting this will *not* happen in the official codebase.

--
Christophe Porteneuve aka TDD
t...@tddsworld.com

Nic Williams

unread,
Sep 11, 2006, 6:24:10 AM9/11/06
to rubyonrail...@googlegroups.com
I agree, and I still use the $('element-id') version in most places. I found 'element-id'.$() reads well within a line of complex code where it becomes less obvious what the $(...) is enveloping.

Cheers
Nic

http://www.drnicwilliams.com - Ruby/Rails blog
skype: nicwilliams
(m) +31 62 494 8552
(p) +61 7 3102 3237 (finds me anywhere in the world)
(f) +61 7 3305 7572 (sends fax to my email)

Thomas Fuchs

unread,
Sep 11, 2006, 9:30:28 AM9/11/06
to rubyonrail...@googlegroups.com
Why not write that as

String.prototype.$ = function() {
return $(this);
}

?

-Thomas

--
Thomas Fuchs
wollzelle

http://www.wollzelle.com

questentier on AIM
madrobby on irc.freenode.net

http://www.fluxiom.com :: online digital asset management
http://script.aculo.us :: Web 2.0 JavaScript
http://mir.aculo.us :: Where no web developer has gone before


Nic Williams

unread,
Sep 11, 2006, 9:50:49 AM9/11/06
to rubyonrail...@googlegroups.com
It didn't seem to work for me when I tried it:

In Firebug:

>>> "element-id".$()
["e", "l","e", "m","e", "n","t", "-","i", "d"]

Any ideas why $(this) on a string defaults to splitting the string into chars?

Thomas Fuchs

unread,
Sep 11, 2006, 10:39:01 AM9/11/06
to rubyonrail...@googlegroups.com
Good question.

>>> function $(){ return arguments[0]; }
>>> String.prototype.$ = function() { return $(this) }
function () {...}
>>> "blah".$()
["b","l","a","h"]
>>> function $(){ return arguments[0].toUpperCase(); }
>>> "blah".$()
"BLAH"

Anyway, no time to work this out,
Thomas

Chris Lear

unread,
Sep 11, 2006, 1:12:59 PM9/11/06
to rubyonrail...@googlegroups.com
* Thomas Fuchs wrote (11/09/06 15:39):

> Good question.
>
>>>> function $(){ return arguments[0]; }
>>>> String.prototype.$ = function() { return $(this) }
> function () {...}
>>>> "blah".$()
> ["b","l","a","h"]
>>>> function $(){ return arguments[0].toUpperCase(); }
>>>> "blah".$()
> "BLAH"


>>> String.prototype.X = function() { return (this) }
>>> "blah".X()


["b","l","a","h"]

>>> typeof "blah".X()
"object"
>>> typeof "blah"
"string"
>>> typeof String("blah")
"string"


Not sure what this proves, except that the function returns a thing with
a type "object" rather than type "string". The way firebug displays
things probably depends on the result of typeof, and arrays give
"object", so Firebug thinks it's an array. Or something like that.

Chris

Nic Williams

unread,
Sep 11, 2006, 1:25:14 PM9/11/06
to rubyonrail...@googlegroups.com
So String(this) works.

String.prototype.$ = function() {
  return $(String(this))
}

>>> "map".$()


Christophe Porteneuve

unread,
Sep 11, 2006, 2:13:49 PM9/11/06
to rubyonrail...@googlegroups.com
Nic Williams a écrit :

> So String(this) works.
>
> String.prototype.$ = function() {
> return $(String(this))
> }
>
>>>> "map".$()
> <div id="map">

Yeah, the issue with your original implementation is due to this test in
Prototype's $:

if (typeof element == "string")

When you refer to yourself, as a String, using this, typeof says
"object". A more reference-proof way of testing whether you actually
are a string would be to say:

if (element.constructor === String)

Using this:

function $() {
var results = [], element;
for (var i = 0; i < arguments.length; i++) {
element = arguments[i];
if (element.constructor === String) {
element = document.getElementById(element);
}
results.push(Element.extend(element));
}
return results.reduce();
}

Your original implementation:

String.prototype.$ = function() { return $(this); }

works well too.

--
Christophe Porteneuve a.k.a. TDD
"[They] did not know it was impossible, so they did it." --Mark Twain
Email: t...@tddsworld.com

Nic Williams

unread,
Sep 11, 2006, 3:28:46 PM9/11/06
to rubyonrail...@googlegroups.com
Ahh. Never knew about .constructor field. Thanks. Nic.

Christophe Porteneuve

unread,
Sep 11, 2006, 3:50:11 PM9/11/06
to rubyonrail...@googlegroups.com
Nic Williams a écrit :

> Ahh. Never knew about .constructor field. Thanks. Nic.

You're welcome :-)

As for .constructor:

- Used in Array.flatten and Form.Element.serialize.
- Specified in ECMA-262 §15.1.4
- Specified in DevMo:

http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Function:constructor
http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Object:constructor

BETTER YET:

Another way to get this result would be to use the instanceof operator,
which is admittedly more readable (shame on me!):

if (element instanceof String)

- Used in Object.inspect
- Specified in ECMA-262 §11.8.6
- Specified in DevMo:

http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Operators:Special_Operators:instanceof_Operator

I'll offer a patch to Prototype around this.

Nic Williams

unread,
Sep 11, 2006, 3:56:18 PM9/11/06
to rubyonrail...@googlegroups.com
Except:

>>> "element-id".constructor
function String() {...}

but:
>>> "element-id" instanceof String
false
(http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Operators:Special_Operators:instanceof_Operator )

So would it still be useful for a revised $() function?

On 9/11/06, Christophe Porteneuve <t...@tddsworld.com > wrote:

Dr Nic

unread,
Sep 11, 2006, 4:24:07 PM9/11/06
to Ruby on Rails: Spinoffs
How about $H for objects:

Object.prototype.$H = function() {
return $H(this);
}

This is great for console debugging:

>>> anObject.$H().inspect()
"#<Hash:{'key1': 'value1'}>"

So much fun in 3 lines of code! :)

Nic

Martin Bialasinski

unread,
Sep 11, 2006, 4:48:48 PM9/11/06
to rubyonrail...@googlegroups.com
On 9/11/06, Nic Williams <drnicw...@gmail.com> wrote:

> So would it still be useful for a revised $() function?

There is no need to change $().

What you see is the legacy of Netscape taking a stride away from the
ECMAScript standard.

Netscape's JS engine treated (and still treats) strings as array of
strings to allow things like "test"[0] to return "t".

I do not know the internal working, but from observation:

When using "this" in a function defined in String.prototype, "this"
behaves like a string, when the context of the use requires a string.
And it looks like the engine knows document.getElementById() requires
a string. If the context does not require a string, like when using
"this" as a function argument to a user defined function, "this"
evaluates to an array of chars.

Therefore, the error is not with $(). Besides the before mentioned
$(String(this)), $(this.valueOf()) will work as well.

Martin Bialasinski

unread,
Sep 11, 2006, 4:56:29 PM9/11/06
to rubyonrail...@googlegroups.com
On 9/11/06, Dr Nic <drnicw...@gmail.com> wrote:

> Object.prototype.$H = function() {

Vade retro, Satanas!

This breaks the "Object is an empty container and can be safely looped
with for( in )" convention.

http://erik.eae.net/archives/2005/06/06/22.13.54

Andrew Kaspick

unread,
Sep 11, 2006, 4:57:34 PM9/11/06
to rubyonrail...@googlegroups.com
There has been a previous thread on the overuse of $ type functions.
I can't remember how long ago that was.... 3-4 months?? Sorry, I
don't have any more information about that though.

On 9/11/06, Dr Nic <drnicw...@gmail.com> wrote:
>

Nic Williams

unread,
Sep 11, 2006, 5:00:38 PM9/11/06
to rubyonrail...@googlegroups.com
That's a pity. I've had lovely fun with it in Firebug for the last half hour. :(

Christophe Porteneuve

unread,
Sep 11, 2006, 5:59:47 PM9/11/06
to rubyonrail...@googlegroups.com
Nic Williams a écrit :

> Except:
>
>>>> "element-id".constructor
> function String() {...}
>
> but:
>>>> "element-id" instanceof String
> false

Yeah, saw that. Kinda weird, btw. So we would have to use BOTH, with a ||.

Christophe Porteneuve

unread,
Sep 12, 2006, 2:24:50 AM9/12/06
to rubyonrail...@googlegroups.com
Martin Bialasinski a écrit :

>> Object.prototype.$H = function() {
>
> Vade retro, Satanas!
>
> This breaks the "Object is an empty container and can be safely looped
> with for( in )" convention.

Aside from the '$ overuse' issue, for/in indeed doesn't work too good on
Prototype-extended objects, what with all the methods. Which is
precisely why we now have Object.keys(). You would replace for/in with
Object.keys().each, I guess.

Martin Bialasinski

unread,
Sep 12, 2006, 4:31:52 AM9/12/06
to rubyonrail...@googlegroups.com
On 9/12/06, Christophe Porteneuve <t...@tddsworld.com> wrote:

> Aside from the '$ overuse' issue, for/in indeed doesn't work too good on
> Prototype-extended objects, what with all the methods.

for ( in ) loops are supposed to work with Objects and they do
flawlessly. This is why Object.prototype is not tempered with. for (
in ) loops do not make sense for the other native data types, and user
defined classes can't mark properties non-enumerable anyway (a
ECMAScript omission), so for ( in ) can only be used for introspection
on them.

Object.keys() is not there because for ( in ) does not work (it does),
it is a syntactic sugar. If you look at the implementation, it is just
a for ( in ) loop. If it would not work properly, Object.keys() could
not have used it, obviously.

Reply all
Reply to author
Forward
0 new messages