Forum Moderators: open
"Events" trigger Javascript.
In addition to a user event like clicking the mouse, an event could be as straight ahead as the page being read into memory by the browser, which is the pseudo-event that triggers "naked" scripts (scripts not enclosed in a function or limited to a particular element, as the example below is limited.).
The events do not create any extra script.
So:
"onmouseup" is an event
"top.status='hi there';return true;" is a script
onmouseup="top.status='hi there';return true;"
is a bit of code that instructs a script-capable browser to execute the script whenever the "onmouseup" event occurs within the context of the element containing the instruction. (In this case, the browser window would not be updated without the "return true", which instructs it to update its status widget with the new text immediately upon receipt of the "return" instruction.)
The browser application is responsible for recognizing events, and doing something (or nothing) with them. This functionality is built into the browser program. The browser then passes the event to its internal Javascript parsing functionality. At that point, the browser's internal Javascript parsing functionality decides how to act on the instruction, and how to pass the changes to its window widget for screen redrawing (rendering). This activity also uses some operating system widgets to accomplish its goal (such as redraw instructions to the monitor, text rendering from the user's font library, and so forth.)
For instance :-
<a id="test" .... onclick="alert('yikes');" ....>
typeof(test.onclick) is 'object'
but if I say
function fnx()
{...}
id.test.onclick = fnx
typeof(test.onclick) is 'function'
So far, the code I have implemented seems to work but there are behavioural anomalies and I need to understand exactly what is going on.
Kaled.
clickMe.onClick = new Function("js_code");
typeof for a Function should be function, not object. It's suprising that you're seeing object. I can't replicate what you're seeing under Firefox or IE.
You might check it for an .arity property, which Functions have. Also it should toString() as the literal text of the function.
My code seems to be working now in Firefox, Opera and IE. However, it is neither finished nor fully tested.
I saw typeof() = 'object' in Firefox 0.9.2 - I tend to use FF or Opera first for script development.
Discovered another issue with FF.
function test(p)
{alert(arguments.length + typeof(p)) }
test();
should display '0undefined' and, in fact, it does, but in a more complex piece of code it displays '1object'. The same code works correctly in Opera and IE.
Thanks again,
Kaled.
<a id="test" .... onclick="alert('yikes');" ....>typeof(test.onclick) is 'object'
but if I say
function fnx()
{...}id.test.onclick = fnx
typeof(test.onclick) is 'function'
Within the realm of my first post ("..in the context of the element.."), this seems entirely correct.
In your first example, the onclick event made a call to the browser's window widget/object. In the second, the onclick event made a call to a function.
Regarding your note about additional issues with the size of the array returned and the definition of the argument, what are you passing to the function as "p"? Is it a call to a function, or a call to a widget?
Returning to to the event problem :-
My problem is that I need to temporarily insert an additional event handler that is called before any other assigned event. And I want a generic solution. By using eval, strings for event names and saving the old event handler as an additional property, everything can be made to work ok. Fortunately the following works to call the saved event even if it is assigned in HTML.
ctrl.savedEvent();
It seems to work on all browsers though I'm not sure I understand why if savedEvent is an object (in Firefox at least - not sure about other browsers)
Kaled.
My problem is that I need to temporarily insert an additional event handler that is called before any other assigned event. And I want a generic solution. By using eval, strings for event names and saving the old event handler as an additional property, everything can be made to work ok.
Since functions are objects, you could make the run-first function itself handle all of the details of preempting an event handler. If you're comfortable with the idea of functions having methods, that is. :) I don't know the details of your environment, but this approach uses no strings or evals.
In this implementation, any function can preempt the event handlers of an arbitrary number of objects. A function can even preempt another preempting function. (But not itself.)
The preempting event handler function just needs a couple of lines of code to restore & run the original event handler. It could be simpler, but there are some magic scoping rules that disallow visibility to custom Function properties and methods from within a function. Which is probably pretty reasonable.
<html>
<head>
<script type="text/javascript">
function F_ehRunFirst(element, type) {
if (! this.handlers) this.handlers = {};
if (! this.handlers[element]) this.handlers[element] = {};
this.handlers[element][type] = element[type];
element[type] = this;
}
function F_ehRestore(e) {
el = (window.event)? e.srcElement : e.target;
type = 'on' + e.type;
el[type] = this.handlers[el][type];
this.handlers[el][type] = null;
if (window.event)
return el[type].call(window);
else
return el[type].call(window, e);
}
Function.prototype.handlers = null;
Function.prototype.ehRunFirst = F_ehRunFirst;
Function.prototype.ehRestore = F_ehRestore;
function aHandler(e) {
if (!e) e = window.event;
alert("I am the preempting handler.");
el = (window.event)? e.srcElement : e.target;
return el['on' + e.type].ehRestore(e);
}
function setOneShot(id) {
el = document.getElementById(id);
aHandler.ehRunFirst(el, 'onclick');
alert("Link primed to run one-shot handler first.");
}
</script>
</head>
<body>
<p>
<a id="alink" href="#" onclick="alert('I am the literal handler.');">a link with an onclick</a>
</p>
<p>
<a href="#" onclick="setOneShot('alink')">Preempt literal handler.</a>
</p>
</body>
</html>
.handlers) When a function overrides an event handler, it stashes the original event handler in this map as
this.handlers.element.eventType = originalEvent. It then drops itself into the element's event handler slot.
When the event fires, the overidding function is in the event handler slot, so it can do whatever it wants. When it's done, it just needs to reach into that map again and call the original event handler. If you want the override to be one-shot, you can restore the original event handler at the same time. Or you could leave everything as is if you always want your function to run first.
The element map I wrote is probably broken. My JavaScript is a little rusty, but I have a nagging memory that array keys can only be strings. If that's the case, then
.handlers[element]is actually compiling as
.handlers[element.toString()]. If that's the case, you can build a slightly different map structure to accomplish the same goal.
It's also possible to do all of this without adding methods to Function via the
prototype, but you do need at least the
.handlersproperty to be added to functions.