On Oct 22, 5:42 am, "T.J. Crowder" <[EMAIL PROTECTED]> wrote:
> @All:
>
> You know how you plan to send a really brief, simple reply and things
> just get out of hand?  Apologies for the long post...
>
> @RobG:
>
> > doSomething($('elID'))
>
> I think he means using Prototype's element extensions (though I could
> be mistaken).  Naturally if you like, you can code your own methods to
> handle being passed an undefined element.  But while you can do this:
>
> Element.toggle('elID');
>
> ...it will still throw an error, as most of Prototype's functions
> don't guard against undefined elements.  (*Appropriately* don't, in my
> view; to me 90% of the time that's an application logic error.)
>
> @OP:
>
> If you really want this "nullsafe" behavior, there are at least three
> ways I can think of to get it, two of which come with a significant
> runtime cost:
>
> 1. Modify your copy of Prototype.js:
>
> Figure you'll have to do this about twice a year as you deploy new
> versions of Protoype, but that's not that bad, is it?  Basically
> taking (say) Element.toggle:
>
>   toggle: function(element) {
>     element = $(element);
>     Element[Element.visible(element) ? 'hide' : 'show'](element);
>     return element;
>   },
>
> ...and inserting some code to fail silently if the element doesn't
> exist:
>
>   toggle: function(element) {
>     element = $(element);
>     if (!element) return undefined; // <=== New code
>     Element[Element.visible(element) ? 'hide' : 'show'](element);
>     return element;
>   },
>
> That means that "Element.toggle('elID')" will fail silently rather
> than raising an error.  (Naturally "$('elID').toggle()" will still
> raise an error, since that's trying to call a function on an undefined
> reference.)
>
> 2. Be explicit with a wrapper
>
> In my view, in the normal case if you're passing an undefined
> reference, it's an application logic error and so you *want* Prototype
> to throw an error.  But there are certainly other cases where the
> thing you want to act on can *correctly* either exist or not.  So why
> not be explicit about it in your code:
>
> function nscall(func, element) {
>     var args;
>
>     element = $(element);
>     if (!element)
>     {
>         return undefined;
>     }
>     args = $A(arguments);
>     args.shift();
>     args[0] = element;
>     return func.apply(this, args);
>
> }

I respect your style, T.J., but things like "return undefined" always
throw me off as being a bit redundant : ) Functions return undefined
implicitly when nothing is specified, so why not take advantage of
that?

My biased little version would be among these lines:

function nscall(func, element) {
  if (element = $(element)) {
    return func.apply(null, $A(arguments).slice(1));
  }
}

We could also replace $A with `Array.prototype.slice.call` as a minor
speed improvement.

>
> In places where it's valid that the element may not exist, instead of:
>
>     $('elID').toggle();  or  Element.toggle('elID');
>
> ...it would be
>
>     nscall(Element.toggle, 'elID');
>
> You're being clear that it's okay if the element doesn't exist.
>
> The runtime cost here is obviously several additional function calls,
> both to nscall() and within it.
>
> 3. Wrap Prototype's Functions
>
> Finally and most intrusively, you can always wrap the functions for
> which you want the behavior, using Function.wrap[1]:
>
> Element.toggle = Element.toggle.wrap(function(originalFunction,
> element) {
>     element = $(element);
>     return element ? originalFunction(element) : undefined;
>
> });
>
> That makes "Element.toggle('elID')" fail quietly if there is no
> element with the ID 'elID'.  (Again, the methodized version "$
> ('elID').toggle()" would of course still fail.)
>
> You'd have to do this for all functions that you want to have behave
> that way, but I'm guessing it's probably a subset of the full Element
> API and even if not, that API doesn't change so frequently that it's a
> massive issue updating your list when you deploy a new Prototype
> version.  But if you REALLY want to make it easy, here's a barely-
> tested function that takes a space-delimited list of Element methods
> and wraps them to make them fail silently if the element is undefined:
>
> $w('toggle addClassName').each(function(fname) {
>     Element[fname] = Element[fname].wrap(function(originalFunction,
> element) {
>         var args;
>
>         element = $(element);
>         if (!element)
>         {
>             return undefined;
>         }
>         args = $A(arguments);
>         args.shift();
>         args[0] = element; // We've already looked it up, save doing
> it again
>         return originalFunction.apply(this, args);
>     });
>
> });
>
> (In that example, I've only wrapped 'toggle' and 'addClassName' --
> although I think addClassName already fails silently.)
>
> Breaking that down, we use the $w() utility function[2] to turn the
> space-delimited list into an Array, then we use Enumerable.each[3]
> (which is mixed into Array) to process each item in that Array.
> Inside our iterator, Element[fname] references the function, and so we
> wrap each of those using the given wrapper.  The wrapper checks if the
> element exists and if so, passes on the element and any other
> arguments to the original function.  (Note the args.shift(), since of
> course the first argument to our wrapper is the original function.)
>
> Again, the runtime cost there is significant and I don't think I'd do
> it.

I think `wrap` option is least obtrusive of the ones you presented. I
also think that `if (foo) bar(...)` (or, perhaps, `foo && bar(...)`)
is the best way to handle `null`/`undefined` values. Best from the
performance, clarity and maintenance points of view.

On the other hand, this problem somewhat disappears once element
wrappers are introduced (as the ones we had lengthy discussions about
in the past). When every method is guaranteed to return a reference to
a wrapper - existence of an element/collection is of little importance
(besides runtime/memory costs)

>
> [1]http://www.prototypejs.org/api/function/wrap
> [2]http://www.prototypejs.org/api/utility/dollar-w
> [3]http://www.prototypejs.org/api/enumerable/each
>
> HTH,
> --
> T.J. Crowder
> tj / crowder software / com

[snip]

--
kangax
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to prototype-scriptaculous@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to