Forum Moderators: open

Message Too Old, No Replies

How are HTML/JS attribute events implemented?

I' doing something deep and need to understand this.

         

kaled

1:31 pm on Oct 20, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



<A id="clickMe" ... onclick="js_code" .....>

How, precisely is the code above implemented? Does it work like this?

function x73_onclick() { eval("js_code") }

clickMe.onclick = x73_onclick;

In other words, is an invisible javascript function created?

Kaled.

StupidScript

8:17 pm on Oct 20, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



In simple terms:

"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.)

kaled

10:53 pm on Oct 20, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I understand what events are, I need to know how events that are assigned in HTML are implemented internally.

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.

jollymcfats

7:00 am on Oct 21, 2004 (gmt 0)

10+ Year Member



Literal event handlers are anonymous functions. They're more like this:

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.

kaled

9:45 am on Oct 21, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Thanks,

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.

StupidScript

4:36 pm on Oct 21, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



<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?

kaled

12:29 am on Oct 22, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



The argument problem arises when fewer parameters are passed than are declared. However, there must be more to it than that since mostly FF works correctly. I have yet to work out what the magic ingredient is to expose this fault. If I do so I'll post it here and report it to Mozilla.

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.

jollymcfats

6:26 pm on Oct 22, 2004 (gmt 0)

10+ Year Member



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>

kaled

7:40 pm on Oct 22, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Thanks,

That looks like it'll do exactly what I want, but it's going to take me a while to figure out how it works - there's stuff in there that I've never used.

Kaled.

jollymcfats

8:08 pm on Oct 22, 2004 (gmt 0)

10+ Year Member



The general idea is to keep a map of overridden event handlers as a property of the function itself. (
.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
.handlers
property to be added to functions.

kaled

11:22 pm on Oct 22, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Thanks again.

I've pasted your explanation into the file I've saved.
I'll be away for a few days - if I have time I'll see if I can get it working.

My solution works ok (for a single event) but it's not very pretty and I have concerns regarding browser compatibility.

Kaled.

StupidScript

1:02 am on Oct 23, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Nice, jollymcfats. Much more practical than my shenanigans! Lots to learn ... :)