Forum: Rails Spinoffs (closed, excessive spam) Layout of Javascript code - how do you guys do it?

7f896bb1e7ba6c95f286f74d4c210ac8?d=identicon&s=25 sheps-ii (Guest)
on 2008-06-04 19:32
(Received via mailing list)
Ok so Prototype effectively says that Javascript code anywhere in an
HTML file is not cool, e.g. you should use observe to give HTML
elements listeners which fire functions.

But then where should the code start? Let me elaborate. Given the
first assumption, I now have no javascript in my HTML save the files
included in the <head>. But, for me to start JavaScript execution in
one of these files is useless - if I try to use observe() then
obviously the rest of the page hasn't loaded yet, and the script
terminates.

So how do people layout their code? Surely you have to have <body
onload="..."> or something to let the Javascript know when it can fire
away?

Thanks in advance for all thoughts and advice.
47555008c5f66348b8673b9c90e4c394?d=identicon&s=25 Hector Virgen (Guest)
on 2008-06-04 19:38
(Received via mailing list)
I usually wrap my event listeners in a global event listener that fires
when the page has fully loaded.

Event.observe(window, 'load', function(event) {
    // Place all element event listeners here
});

The problem with this is that it doesn't run until the page has fully
loaded (including downloading all images on the page).

More information on this is available at
http://www.prototypejs.org/api/event/observe

-Hector
625e99cca23b3893a070c84dc3b0ac13?d=identicon&s=25 Justin Perkins (Guest)
on 2008-06-04 19:49
(Received via mailing list)
You want to use:

document.observe('dom:loaded', yourObserver);

From: http://www.prototypejs.org/api/document/observe
7f896bb1e7ba6c95f286f74d4c210ac8?d=identicon&s=25 sheps-ii (Guest)
on 2008-06-04 20:51
(Received via mailing list)
Thank you, that's fantastic. I love how prototype helps true
modularity into content(server side)->structure (html)->behaviour
(js). Cheers!
5bf4a96e583a502a810ad66fc343a60f?d=identicon&s=25 Christophe Porteneuve (tdd)
on 2008-06-04 22:00
(Received via mailing list)
The basic, fundamental way everywhere in the examples (and in the books)
is to register your observers within a dom:loaded observer.  Something
like this in your scripts:

...

function initXYZObservers() {
   ...
}

...

function initABCObservers() {
   ...
}

...

document.observe('dom:loaded', function() {
   initXYZObservers();
   initABCObservers();
});

dom:loaded will trigger once the DOM is fully loaded, but before
external resources, such as images and stylesheets, have loaded.  It's
an ideal place to register your observers unobtrusively ASAP.

You'd see it everywhere in my book ;-)

   http://books.pragprog.com/titles/cppsu/

'HTH

--
Christophe Porteneuve aka TDD
tdd@tddsworld.com
F2c683e7dc0c4d4bcc790f87eaa67301?d=identicon&s=25 RobG (Guest)
on 2008-06-05 14:36
(Received via mailing list)
On Jun 5, 5:59 am, Christophe Porteneuve <t...@tddsworld.com> wrote:
> The basic, fundamental way everywhere in the examples (and in the books)

Presumably you forgot the phrase "...about Prototype.js...".  :-)

> is to register your observers within a dom:loaded observer.  Something
> like this in your scripts:
[...]
> You'd see it everywhere in my book ;-)

There are other opinions (noting that moving scripts to the bottom of
the page also removes the need to use the sometimes problematic
dom:loaded):

<URL:
http://developer.yahoo.net/blog/archives/2007/07/h...
>


--
Rob
5bf4a96e583a502a810ad66fc343a60f?d=identicon&s=25 Christophe Porteneuve (tdd)
on 2008-06-05 15:22
(Received via mailing list)
RobG a écrit :
> There are other opinions (noting that moving scripts to the bottom of
> the page also removes the need to use the sometimes problematic
> dom:loaded):
>
> <URL: http://developer.yahoo.net/blog/archives/2007/07/h...

Yup, Yahoo! recommends bottom script inits; I usually don't bother, as
dom:loaded works alright for me, and I prefer keeping my scripts out of
my HTML.  But I can see the need would arise sometime.

--
Christophe Porteneuve aka TDD
tdd@tddsworld.com
9d1db4326350cea545c52f11a2bfc3b2?d=identicon&s=25 nlloyds (Guest)
on 2008-06-06 22:35
(Received via mailing list)
On Jun 4, 1:50 pm, sheps-ii <simonrsheph...@googlemail.com> wrote:
> Thank you, that's fantastic. I love how prototype helps true
> modularity into content(server side)->structure (html)->behaviour
> (js). Cheers!

To take it a step further, I usually encapsulate everything in a class
or namespace. So, the bottom of the html looks like this:

<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="mysite.js"></script>
</body>
</html>

Then the mysite.js:

if (typeof Prototype == "object") { // only load if Prototype is
present
  var MySite = Class.create({
    initialize : function () {
     ... // stuff to do on page load

     this.addObservers();
    },

    // Add additional event handlers here
    addObservers : function () {
      ...
    },

    ...

  });
  document.observe("dom:loaded", function () { new MySite() });
}

I don't know if that's the best way to do things, but It works well
for me. YMMV
F1fcf834ddad415f60d29c87cc10d4d4?d=identicon&s=25 kangax (Guest)
on 2008-06-06 23:13
(Received via mailing list)
typeof unfortunately returns "object" when applied to null
typeof null; // "object"

A safer way is to test for undefined:

if (typeof Prototype != 'undefined') {
  ...
}

- kangax
Af85e01e19b6caa679621b7b783a598f?d=identicon&s=25 T.J. Crowder (Guest)
on 2008-06-07 08:58
(Received via mailing list)
Or "if (Prototype) { ... }" ?
--
T.J. Crowder
tj / crowder software / com
F1fcf834ddad415f60d29c87cc10d4d4?d=identicon&s=25 kangax (Guest)
on 2008-06-07 17:16
(Received via mailing list)
Or that : )
It's also pretty common to check for prototype version:

if (Prototype && Prototype.Version &&
Prototype.Version.indexOf('1.6') != -1) {
  ...
}

- kangax
F490e41cd5de1393a9f6958b70dae6ab?d=identicon&s=25 Frederick Polgardy (Guest)
on 2008-06-07 19:01
(Received via mailing list)
I thought this would throw an exception if Prototype wasn't defined.
You
would need to say:

var Prototype
if (Prototype) { ... }

Right?

-Fred

On Sat, Jun 7, 2008 at 1:58 AM, T.J. Crowder <tjcrowder@gmail.com>
wrote:

> > A safer way is to test for undefined:
> > > On Jun 4, 1:50 pm, sheps-ii <simonrsheph...@googlemail.com> wrote:
> > > </body>
> > >      this.addObservers();
> > >   document.observe("dom:loaded", function () { new MySite() });
> >
> > > }
> >
> > > I don't know if that's the best way to do things, but It works well
> > > for me. YMMV
> >
>


--
Science answers questions; philosophy questions answers.
F1fcf834ddad415f60d29c87cc10d4d4?d=identicon&s=25 kangax (Guest)
on 2008-06-07 19:10
(Received via mailing list)
Never heard of that, Fred.

You are not accessing any properties, nor performing any operations on
a variable.
It would simply yield undefined which would then evaluate to false.

- kangax
Af85e01e19b6caa679621b7b783a598f?d=identicon&s=25 T.J. Crowder (Guest)
on 2008-06-08 11:23
(Received via mailing list)
Hi Fred,

No, not in standard JavaScript.  The interpreter will work its way up
the scope chain looking for "Prototype"; if it reaches the global
object (which is always the root of the scope chain; the global object
in browser apps is the window object), it assumes the reference is a
property of that object.  The property will have the value undefined.

That's why people keep getting into trouble with implicit global
variables (e.g., forgetting to declare them), like this:

function foo(stringArray)
{
    for (i = 0; i < myArray.length; ++i)
    {
        bar(stringArray);
    }
}

function bar(element)
{
    i = element.indexOf(',');
    if (i > 0)
    {
        $(element.substring(0, i)).update(element.substring(i + 1));
    }
}

Since 'i' isn't declared anywhere, it implicitly becomes a property of
the global object (window), and hence the above code blows up because
the 'i' in foo() and the 'i' in bar() are the same property
(window.i), and so bar() stomps on the array iterator in foo().

More in my sadly-neglected blog:
http://blog.niftysnippets.org/2008/03/horror-of-im...
--
T.J. Crowder
tj / crowder software / com
Af85e01e19b6caa679621b7b783a598f?d=identicon&s=25 T.J. Crowder (Guest)
on 2008-06-08 11:26
(Received via mailing list)
Sorry, copy-and-paste error on the entire foo() function introducing
not one but two errors; it should be:

function foo(stringArray)
{
    for (i = 0; i < stringArray.length; ++i)
    {
        bar(stringArray[i]);
    }

}

-- T.J. :-)
7a0e72a6f55811246bb5d9a946fd2e49?d=identicon&s=25 Радослав Станков (Guest)
on 2008-06-08 12:31
(Received via mailing list)
I use something like lowpro's behaviors (
http://svn.danwebb.net/external/lowpro/tags/rel_0....)
and http://encytemedia.com/event-selectors.My code looks like that and
I haven't use write js in the htm file for years:

CD3.Behaviors({
    '#someElement': {
        'click':                Site.onClick,
        'custom:event'    Site.onCustomEvent
    },
    'select': CD3.Select, // this CD3.Select is class
    'ul li.tab:click: Site.activateTab
});


This kind of work helps my to separate my logic from view. It also
helps when some other developers comes to the project.

p.s. http://next.pixeldepo.com/files/behaviors.js <- here is my
CD3.Behaviors but if someone is interested.
F490e41cd5de1393a9f6958b70dae6ab?d=identicon&s=25 Frederick Polgardy (Guest)
on 2008-06-08 15:46
(Received via mailing list)
It seems like that would be the case based on JavaScript's lexical
scoping
rules, but in Firefox I get a JavaScript error ("ReferenceError:
Prototype
if not defined") when I try to alert(Prototype) or if(Prototype) { ...
}.

Didn't make sense to me either. :-)

-Fred

On Sun, Jun 8, 2008 at 4:22 AM, T.J. Crowder <tjcrowder@gmail.com>
wrote:

>
> Hi Fred,
>
> No, not in standard JavaScript.  The interpreter will work its way up
> the scope chain looking for "Prototype"; if it reaches the global
> object (which is always the root of the scope chain; the global object
> in browser apps is the window object), it assumes the reference is a
> property of that object.  The property will have the value undefined.


--
Science answers questions; philosophy questions answers.
Af85e01e19b6caa679621b7b783a598f?d=identicon&s=25 T.J. Crowder (Guest)
on 2008-06-09 10:25
(Received via mailing list)
Wow.  Thanks, Fred, I was quite wrong on this.  This fails with a
ReferenceError:

function go()
{
    if (Flibbergibbet)
    {
        alert("Flibbergibbet found");
    }
    else
    {
        alert("Flibbergibbet not found");
    }
}

...whereas this works:

function go()
{
    if (typeof Flibbergibbet == "undefined")
    {
        alert("Flibbergibbet found");
    }
    else
    {
        alert("Flibbergibbet not found");
    }
}

...saying "Flibbergibbet not found"; and interestingly, this also
works:

function go()
{
    if (window.Flibbergibbet)
    {
        alert("Flibbergibbet found");
    }
    else
    {
        alert("Flibbergibbet not found");
    }
}

...saying "Flibbergibbet not found".

The relevant sections of the ECMA spec seem to be 8.7 ("The Reference
Type") and 10.1.4 ("Scope Chain and Identifier Resolution").  If you
read 8.7, then read 10.1.4, this is exactly the behavior we're
seeing.  It's as spec'd.  Summarizing:

1. Retrieving the value from an unqualified identifier does *not*
resolve to a property of the global object, it's an error.

2. Assigning a value to an unqualified identifier creates a property
on the global object.  (Yes, this is assymmetric.)

3. Retrieving the value of a *qualified* identifier (e.g.,
"window.Flibbergibbet") resolves to a property on the given object and
gives you the value undefined if it doesn't exist.

E.g., there's a fundamental difference between "if (Flibbergibbet)"
and "if (window.Flibbergibbet)", but not between "Flibbergibbet = 5"
and "window.Flibberbigget = 5".

You learn something new every day.  Thanks again, Fred!
--
T.J. Crowder
tj / crowder software / com
F490e41cd5de1393a9f6958b70dae6ab?d=identicon&s=25 Frederick Polgardy (Guest)
on 2008-06-09 19:53
(Received via mailing list)
Hey, it's all good.  Thanks for doing the legwork.  I probably wouldn't
have
gone to the spec to figure out why it didn't work. ;-)

-Fred

On Mon, Jun 9, 2008 at 3:24 AM, T.J. Crowder <tjcrowder@gmail.com>
wrote:

>
> Wow.  Thanks, Fred, I was quite wrong on this.  This fails with a
> ReferenceError:


--
Science answers questions; philosophy questions answers.
F2c683e7dc0c4d4bcc790f87eaa67301?d=identicon&s=25 RobG (Guest)
on 2008-06-10 05:23
(Received via mailing list)
On Jun 8, 11:45 pm, "Frederick Polgardy" <f...@polgardy.com> wrote:
> On Sun, Jun 8, 2008 at 4:22 AM, T.J. Crowder <tjcrow...@gmail.com> wrote:
>
> > Hi Fred,
>
> > No, not in standard JavaScript.  The interpreter will work its way up
> > the scope chain looking for "Prototype"; if it reaches the global
> > object (which is always the root of the scope chain; the global object
> > in browser apps is the window object), it assumes the reference is a
> > property of that object.  The property will have the value undefined.

No, it won't.  There is a big difference between a property whose
value is undefined (as in a declared variable that has not been
assigned a value) and an indetifier that has not been defined at all
(i.e. not declared).

Read the ECMAScript spec, section 10.1.4.  To summarise, if a property
with the same name as the identifier is not found on the scope chain,
then return "a value of type Reference whose base object is null and
whose property name is the Identifier".

Having resolved the identifier, it now must be evaluated, section 12.4
says:

1. Evaluate Expression.
2. Call GetValue(Result(1)).
3. Return (normal, Result(2), empty).

When it gets to step 2 and calls GetValue...  Section 8.7.1 getValue
says:

1. If Type(V) is not Reference, return V.
2. Call GetBase(V).
3. If Result(2) is null, throw a ReferenceError exception.

Ta da.


> It seems like that would be the case based on JavaScript's lexical scoping
> rules, but in Firefox I get a JavaScript error ("ReferenceError: Prototype
> if not defined") when I try to alert(Prototype) or if(Prototype) { ... }.
>
> Didn't make sense to me either. :-)

Hopefully this order of posing makes more sense.  :-)

It makes sense if you realise that undeclared variables are created as
global variables *only* when code is executed that assigns some value
to them.

e.g. the statement:

  Prototype;

creates a reference error because when attempting to resolve the
identifier "Prototype", it can't be found and so returns a null
reference.

ECMAScript spec sections 12.4,  and

Since JavaScript is a forgiving language, using:

  Prototype = 'foo';

creates a Prototype property of the global object *when the statement
is executed* and the value 'foo' is assigned to it (sections 11.13.1
and 8.7.2 explain).


--
Rob
F490e41cd5de1393a9f6958b70dae6ab?d=identicon&s=25 Frederick Polgardy (Guest)
on 2008-06-10 05:27
(Received via mailing list)
Yup, exactly what TJ said in his lengthy reply to my original message.
:-)

On Mon, Jun 9, 2008 at 10:22 PM, RobG <rgqld@iinet.net.au> wrote:

> 2. Call GetValue(Result(1)).
> 3. Return (normal, Result(2), empty).
>
> When it gets to step 2 and calls GetValue...  Section 8.7.1 getValue
> says:
>
> 1. If Type(V) is not Reference, return V.
> 2. Call GetBase(V).
> 3. If Result(2) is null, throw a ReferenceError exception.
>
> Ta da.


--
Science answers questions; philosophy questions answers.
Af85e01e19b6caa679621b7b783a598f?d=identicon&s=25 T.J. Crowder (Guest)
on 2008-06-10 07:38
(Received via mailing list)
@Rob: Jinx!

-- T.J. ;-)
This topic is locked and can not be replied to.