Problems with dynamically adding events to new DOM objects

C

Chris Lieb

I am trying to add an event listener to the keyup event for some text
inputs that I am creating dynamically. I have no problems getting it
to work in Firefox, but IE just ignores them. I have created a few
functions to aid in making this process work semi-cross-browser. (I
develop in FF, but everyone who uses it will be using IE6.)

function attachEvent(_obj, _evt, _fnc) {
_evt = _evt.toLowerCase();

if (_obj.attachEvent) {
_obj.attachEvent('on' + _evt, _fnc);
} else {
eval('_obj.on' + _evt + ' = ' + _fnc);
}
}

function generateFunction(_name, _params) {
var func;
if (navigator.userAgent.toLowerCase().indexOf('gecko') != -1) {
func = eval('function(){' + _name + '(' + _params + ');}');
} else {
func = function(){eval(_name + '(' + _params + ')');};
}
return func;
}

function attachValidator(_obj, _validatorName) {
if (navigator.userAgent.toLowerCase().indexOf('gecko') != -1) {
attachEvent(_obj, 'keyup', generateFunction(_validatorName, 'this'));
} else {
var eventAttr = document.createAttribute('onkeyup');
eventAttr.value = _validatorName + '(this);';
_obj.setAttributeNode(eventAttr);
}
}

function createTextField(_base, _id, _value, _validatorName, _class) {
var input = document.createElement('input');
var inputID = document.createAttribute('id');
inputID.value = _base + _id;
var inputType = document.createAttribute('type');
inputType.value = 'text';
var inputValue = document.createAttribute('value');
inputValue.value = _value;

input.setAttributeNode(inputID);
input.setAttributeNode(inputType);
input.setAttributeNode(inputValue);

if (_validatorName) {
attachValidator(input, _validatorName);
//attachEvent(input, 'keyup', generateFunction(_validatorName,
'this'));
}

if (_class) {
var inputClass = document.createAttribute('class');
inputClass.value = _class;

input.setAttributeNode(inputClass);
}

return input;
}

Sorry about the browser sniffing using the userAgent string, but since
I am the only non-IE6 user in the building, it shouldn't really matter
in the long run.

Thanks in advance,

Chris
 
L

Lasse Reichstein Nielsen

Chris Lieb said:
I am trying to add an event listener to the keyup event for some text
inputs that I am creating dynamically. I have no problems getting it
to work in Firefox, but IE just ignores them.

Let's quote out of context:
function attachValidator(_obj, _validatorName) {
if (navigator.userAgent.toLowerCase().indexOf('gecko') != -1) {
attachEvent(_obj, 'keyup', generateFunction(_validatorName, 'this'));
} else {
var eventAttr = document.createAttribute('onkeyup');
eventAttr.value = _validatorName + '(this);';
_obj.setAttributeNode(eventAttr);
}
}

So, Gecko based browsers take the first branch and IE will take the
second. If they act differently, this is as good a place to look as
any.

Indeed it seems the second branch doesn't work in IE.

Since the attachEvent function is also written to work in more
than one setting, it looks like an error that you only call it
for Gecko browsers.


When working with HTML it's better to use the HTML DOM properties
(e.g., obj.href) instead of Core DOM attribute methodes (e.g.,
obj.setAttribute). IE, for one, often doesn't react to changes
to Core DOM attributes.
I have created a few functions to aid in making this process work
semi-cross-browser. (I develop in FF, but everyone who uses it will
be using IE6.)

Some suggestions (mostly about how to avoid "eval", which only
complicates your life):
function attachEvent(_obj, _evt, _fnc) {

Try:
function attachEvent(_obj, _evt, _fnc) {
_evt = _evt.toLowerCase();
if (_obj.addEventHandler) {
_obj.addEventHandler(_evt, _fnc, false);
} else if (_obj.attachEvent) {
_obj.attachEvent("on" + _evt, fnc);
} else {
_obj["on" + _evt] = fnc;
}
}
function generateFunction(_name, _params) {

I guess this function's job is to create a function that calls another
with specified arguments. It's safer to pass the actual function than
just its name:

function bindParameter(_func, _params) {
_func.apply(this,_params)
}
var func;
if (navigator.userAgent.toLowerCase().indexOf('gecko') != -1) {
func = eval('function(){' + _name + '(' + _params + ');}');
} else {
func = function(){eval(_name + '(' + _params + ')');};
}

Why this difference? I can't see any feature that IE and Gecko would
do differently in either branch.
return func;
}
function attachValidator(_obj, _validatorName) {

function attachValidator(_obj, _validatorFunc) {
attachEvent(_obj, "keyup", bindParams(_validatorFunc, _obj));
}

function createTextField(_base, _id, _value, _validatorName, _class) {

Here also pass a validator function, not its name.

I.e., something like:
createTextField(..,..,.., function(_obj){ return _obj.value!=""; }, ...);
(or use a function defined somewhere else).

/L
 
C

Chris Lieb

Lasse said:
Let's quote out of context:


So, Gecko based browsers take the first branch and IE will take the
second. If they act differently, this is as good a place to look as
any.

Indeed it seems the second branch doesn't work in IE.

Since the attachEvent function is also written to work in more
than one setting, it looks like an error that you only call it
for Gecko browsers.

The code that is in there for IE does not use the attachEvent method
because I had apready determined that the attachEvent function was not
achieving what I wanted.
When working with HTML it's better to use the HTML DOM properties
(e.g., obj.href) instead of Core DOM attribute methodes (e.g.,
obj.setAttribute). IE, for one, often doesn't react to changes
to Core DOM attributes.
I have created a few functions to aid in making this process work
semi-cross-browser. (I develop in FF, but everyone who uses it will
be using IE6.)

Some suggestions (mostly about how to avoid "eval", which only
complicates your life):
function attachEvent(_obj, _evt, _fnc) {

Try:
function attachEvent(_obj, _evt, _fnc) {
_evt = _evt.toLowerCase();
if (_obj.addEventHandler) {
_obj.addEventHandler(_evt, _fnc, false);
} else if (_obj.attachEvent) {
_obj.attachEvent("on" + _evt, fnc);
} else {
_obj["on" + _evt] = fnc;
}
}
function generateFunction(_name, _params) {

I guess this function's job is to create a function that calls another
with specified arguments. It's safer to pass the actual function than
just its name:

function bindParameter(_func, _params) {
_func.apply(this,_params)
}

I've never seen the apply function before. I looked it up in the
Mozilla Developer docs, but I don't understand how this works or how
the bindParameter function would be used comared to the previous
generateFunction function.
Why this difference? I can't see any feature that IE and Gecko would
do differently in either branch.

For some reason, IE choked on creating an anonymous function using eval
and FF evaluated the IE version correctly, meaning that an anonymous
function was created whose body was eval(_name + '(' + _params + ')');
This did not work in FF because the _name and _params variables had
gone out of scope by the time that the function was invoked.
function attachValidator(_obj, _validatorFunc) {
attachEvent(_obj, "keyup", bindParams(_validatorFunc, _obj));
}



Here also pass a validator function, not its name.

I.e., something like:
createTextField(..,..,.., function(_obj){ return _obj.value!=""; }, ...);
(or use a function defined somewhere else).

I thought that events did not pass any parameters. If this is so,
where is this anonymous function (the one passed to createTextField in
the above example) getting the _obj parameter from?

Thanks for the help, but I am slightly confused by some of the things
that you proposed, such as the bindParam function. Could you please
clarify them?

Chris
 
R

RobG

Chris said:
I am trying to add an event listener to the keyup event for some text
inputs that I am creating dynamically. I have no problems getting it
to work in Firefox, but IE just ignores them. I have created a few
functions to aid in making this process work semi-cross-browser. (I
develop in FF, but everyone who uses it will be using IE6.)
[...]

Aren't you making this much more difficult than it should be? Why
can't you just use:

function createTextField(_base, _id, _value, _validatorName, _class)
{
var oInput = document.createElement('input');
oInput.type = 'text';
oInput.id = _base + _id;
oInput.value = _value;

if ( _validatorName) {
oInput.onkeyup = _validatorName;
}

/* Assuming 'class' is in regard to style, not JScript 'class' */
if (_class && 'string' == typeof _class){
oInput.className = _class;
}

return oInput;
}


Within whatever function is attached to the input, 'this' will refer to
the input that called the function, so if you had:

function showID(){ alert(this.id); }

Then do:

document.body.appendChild(
createTextField( 'foo', 'bar', 'foey', showID, 'brightRed'));


Then you should see a new text input with a value of 'foey' and
displays 'foobar' onkeyup - works in IE, Firefox & Safari at least. If
you want to add multiple functions to a particular handler (which is
rare for onkeyup), use your forking addEventListener/attachEvent for
the _validator.

Sample below:

<html><title>Play</title>

<style type="text/css">
.brightRed {
border: 2px solid red;
background-color: #fdd;
}
</style>

<script type="text/javascript">

function createTextField(_base, _id, _value, _validatorName, _class)
{
var oInput = document.createElement('input');
oInput.type = 'text';
oInput.id = _base + _id;
oInput.value = _value;

if ( _validatorName) {
oInput.onkeyup = _validatorName;
}

/* Assuming 'class' is in regard to style, not JScript 'class' */
if (_class && 'string' == typeof _class){
oInput.className = _class;
}

return oInput;
}

function showID(){ alert(this.id);}

window.onload = function()
{
document.body.appendChild(
createTextField( 'foo', 'bar', 'foey', showID, 'brightRed'));
};

</script>
</html>
 
L

Lasse Reichstein Nielsen

Chris Lieb said:
Lasse Reichstein Nielsen wrote:
The code that is in there for IE does not use the attachEvent method
because I had apready determined that the attachEvent function was not
achieving what I wanted.

Sine no other browser has an attachEvent function (well, maybe some
does by now, but it's a proprietary IE feature), you can safely remove
that code.

I've never seen the apply function before. I looked it up in the
Mozilla Developer docs, but I don't understand how this works or how
the bindParameter function would be used comared to the previous
generateFunction function.

Well, since I forgot something, it's not odd that you don't understand
what it does :)

The bind function should ofcourse return a function, so it should be:

function bindParameter(_func, _params) {
return function() {
_func.apply(this,_params)
};
}

What it now does is to return a function that, when called, calls
the _func function with the parameters in the array _params.

The .apply method on function values calls the function as a method
of the object in the first argument and with the parameters in the
array in the second argument.

There is a similar method, "call", that expects the parameters
directly, not as an array.

So, if o is an object, then the following performs the same calls:

o.foo = myFunc;
o.foo(a,b,c)
and
myFunc.call(o,a,b,c)
and
myFunc.apply(o,[a,b,c])
For some reason, IE choked on creating an anonymous function using eval
and FF evaluated the IE version correctly, meaning that an anonymous
function was created whose body was eval(_name + '(' + _params + ')');
This did not work in FF because the _name and _params variables had
gone out of scope by the time that the function was invoked.

The fragility of eval is one of the reasons it's not recommended.
You have to be very careful when you build source strings for eval,
and it's generally not worth it.
Javascript allows you to pass values around, even function values,
so it's unlikely that you ever need to pass around names of
functions or strings representing values.
Thanks for the help, but I am slightly confused by some of the things
that you proposed, such as the bindParam function. Could you please
clarify them?

Generally, don't use eval.
If you need to call an unknown function in some code, don't pass its
name and look it up, just pass the function directly.

If you want to add an event handler, the W3C DOM method is addEventHandler,
IE's metod is attachEvent (which doesn't bind "this" to the attachee when
it executes the function), and the generic fallback is object.onevent
(i.e., object['on'+_evt]), which can only accept one handler per event
type).

Be carefull with using setAttribute on HTML elements, since some browsers,
most notably IE, will not make them take effect the same way assigning
to the W3C HTML DOM properties does (e.g., _obj.style.width='4px',
_link.href='foo.html').

/L
 
C

Chris Lieb

Lasse said:
Chris Lieb said:
Lasse Reichstein Nielsen wrote:
The code that is in there for IE does not use the attachEvent method
because I had apready determined that the attachEvent function was not
achieving what I wanted.

Sine no other browser has an attachEvent function (well, maybe some
does by now, but it's a proprietary IE feature), you can safely remove
that code.

I've never seen the apply function before. I looked it up in the
Mozilla Developer docs, but I don't understand how this works or how
the bindParameter function would be used comared to the previous
generateFunction function.

Well, since I forgot something, it's not odd that you don't understand
what it does :)

The bind function should ofcourse return a function, so it should be:

function bindParameter(_func, _params) {
return function() {
_func.apply(this,_params)
};
}

What it now does is to return a function that, when called, calls
the _func function with the parameters in the array _params.

The .apply method on function values calls the function as a method
of the object in the first argument and with the parameters in the
array in the second argument.

There is a similar method, "call", that expects the parameters
directly, not as an array.

So, if o is an object, then the following performs the same calls:

o.foo = myFunc;
o.foo(a,b,c)
and
myFunc.call(o,a,b,c)
and
myFunc.apply(o,[a,b,c])
For some reason, IE choked on creating an anonymous function using eval
and FF evaluated the IE version correctly, meaning that an anonymous
function was created whose body was eval(_name + '(' + _params + ')');
This did not work in FF because the _name and _params variables had
gone out of scope by the time that the function was invoked.

The fragility of eval is one of the reasons it's not recommended.
You have to be very careful when you build source strings for eval,
and it's generally not worth it.
Javascript allows you to pass values around, even function values,
so it's unlikely that you ever need to pass around names of
functions or strings representing values.
Thanks for the help, but I am slightly confused by some of the things
that you proposed, such as the bindParam function. Could you please
clarify them?

Generally, don't use eval.
If you need to call an unknown function in some code, don't pass its
name and look it up, just pass the function directly.

If you want to add an event handler, the W3C DOM method is addEventHandler,
IE's metod is attachEvent (which doesn't bind "this" to the attachee when
it executes the function), and the generic fallback is object.onevent
(i.e., object['on'+_evt]), which can only accept one handler per event
type).

Be carefull with using setAttribute on HTML elements, since some browsers,
most notably IE, will not make them take effect the same way assigning
to the W3C HTML DOM properties does (e.g., _obj.style.width='4px',
_link.href='foo.html').

/L

Thanks. That bindParam function has everything working in both FF and
IE.

Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top