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.
on 2008-06-04 19:32
on 2008-06-04 19:38
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
on 2008-06-04 19:49
You want to use:
document.observe('dom:loaded', yourObserver);
From: http://www.prototypejs.org/api/document/observe
on 2008-06-04 20:51
Thank you, that's fantastic. I love how prototype helps true modularity into content(server side)->structure (html)->behaviour (js). Cheers!
on 2008-06-04 22:00
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
on 2008-06-05 14:36
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
on 2008-06-05 15:22
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
on 2008-06-06 22:35
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
on 2008-06-06 23:13
typeof unfortunately returns "object" when applied to null
typeof null; // "object"
A safer way is to test for undefined:
if (typeof Prototype != 'undefined') {
...
}
- kangax
on 2008-06-07 17:16
Or that : )
It's also pretty common to check for prototype version:
if (Prototype && Prototype.Version &&
Prototype.Version.indexOf('1.6') != -1) {
...
}
- kangax
on 2008-06-07 19:01
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.
on 2008-06-07 19:10
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
on 2008-06-08 11:23
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
on 2008-06-08 11:26
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. :-)
on 2008-06-08 12:31
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.
on 2008-06-08 15:46
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.
on 2008-06-09 10:25
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
on 2008-06-09 19:53
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.
on 2008-06-10 05:23
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
on 2008-06-10 05:27
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.