Onfocus input disabled issue on Safari browser

Hello,

I’m fairly new to javascript and I’m having the following issue, on Safari, when the user selects the last radio button (the one that should enable an input field) it does nothing and the input field doesn’t get available for typing.

Can I please ask your help, here is the relevant code:
http://pastebin.com/dTA8yaM7

And here is the attached js file for this document:


function activaInput()
{
    document.candidaturaOnline.txtOutro.disabled=false;
}

function desactivaInput()
{
    document.candidaturaOnline.txtOutro.disabled=true;
}

Please advice, I’m clueless about the issue. The xhtml is validated transitional on w3c.

Regards,
Márcio

First, is this only Safari? All other browsers are doing excellent?

Have you make sure the onfocus is working correctly by throwing in something simple like
function activaInput() {
alert(“HI I HAVE THE FOCUS”);
}

I mean, if it’s ONLY Safari (and maybe Chrome then too) then if the alert doesn’t show, then the problem is the event.

If it does show, then the problem is the line of JS code itself. There are several ways to reference a form control in JS… I don’t know but possibly Safari has trouble with one of them? (though I haven’t heard of one).

…Though I have to say, Javascript might be nice here (and it’s good to learn on stuff like this, this is the sort of thing my boss would ask me to do), but because these are radio buttons and not checkboxes, the user is only able to make one choice anyway: if they say they found out about you via a friend, then they really shouldn’t have a reason to fill in the “other way” textbox anyway.

*edit, your first input’s a little strange, but maybe it’s only in the pastebin? <input type=“radio” onfocus=“desactivaInput();” id=“contactoGomodels” name=“comoConheceu” value=“” ?> /> see the ? in there.

Apart from the rogue ?> in the first input, the lavel for the first input is for “contactoEmpresa” when it should be for “contactoGomodels” instead.

That’s not going to solve your problem though.

The problem is that the onfocus event doesn’t trigger. A more reliable event when using forms is onclick instead.


<input type="radio" onclick="activaInput();" id="outro" name="comoConheceu" />

Secondly, document.candidaturaOnline doesn’t exist. I think that instead you may be wanting document.form.candidaturaOnline and then to get the element called txtOutro

For example:


function activaInput()
{
    document.forms.candidaturaOnline.elements.txtOutro.disabled = false;
}

Then later on, you may want to remove the inline event attribute from within your form, and to control things from scripting instead.

For example: this script attaches on to the onclick event for the entire form, and only if the click occurred on one of the radio buttons, does it set the disabled attribute of txtOutro

The form fields have no inline script events:


<input type="radio" id="contactoGomodels" name="comoConheceu" value="" />
...

The script, just before the </body> tag, uses best-practice cross-browser methods to retrieve the element that was clicked on, and set the disabled property of txtOutro


var form = document.getElementById('candidaturaOnline');
form.onclick = function (evt) {
    evt = evt || window.event;
    var targ = evt.srcElement || evt.target;
    if (targ.nodeType === 3) {
        targ = targ.parentNode;
    }
    if (targ.nodeName === 'INPUT' && targ.name === 'comoConheceu') {
        this.elements.txtOutro.disabled = (targ.id !== 'outro');
    }
}

Thanks a lot, and sorry for this late reply,

I will give it a try latter on and I will post back the results.

Regards,
Márcio

onclick is more reliable than onfocus? Is that because they’re radio buttons? Does selecting the radio with the spacebar trigger “onclick”?

Yes, with the onclick event, using keyboard navigation to arrow through the radio selections triggers the onclick event on the selected radio button.

What this means is, that the field becomes available, and is disabled, depending on which radio button that your keyboard navigation selects.

Absolutely.

It seems a nice way of doing things.

So, no more load of js script on the head section of the page?

At this moment, and just grabbing the code provided:
I’m having a “form is null” and it seems to occur on this line:

form.onclick = function (evt)

Thanks a lot for your feedback, please see if my comments are precise, I really need to speak the right terms at this time, so that I can search for the right solutions.

Regards,
Márcio

//Here we are attributing a function (that accepts one argument) to a specific event on our form. Is this precise?
form.onclick = function (evt)

when the onclick event happens on the form, run this function (with the event as the argument).

//what does it do here? Equals the evt var to himself and to a window.event, but, what is the propose?
evt = evt || window.event; 


//and here? Are we giving two values to one variable?
var targ = evt.srcElement || evt.target;

This is getting around Internet Exploder. If IE is running this, it won’t understand “evt” but will understand “window.event” so this is a way of cross-browsering the variables. Browsers who understand the first one will skip the second, and browsers who don’t understand the first one will check the second one.

//I have seen some nodeTypes but no numeric values attached,

//does this means select “Radio Buttons” ?

No, this is getting around IE again (tho IE is the one who did it right as far as I’m concerned). When walking through the HTML DOM, the normally-smarter browsers, the ones who follow the W3C (like FF etc), they see newlines and whitespace as “nodes”, specifically text nodes (when they shouldn’t, arg) while IE does not (IE only looks at actual elements and the “text nodes” inside them).
when checking for nodeType ===3, you’re checking if the node is a “textNode” which = “3” and we don’t want a newline being called the element… read more: http://www.javascriptkit.com/domref/nodetype.shtml

The JS Paul wrote is making sure all browsers are on the correct element, instead of accidentally on some whitespace or text instead. So, if it’s text or whitespace, look at the parent, and call that the element we care about.

//if nodeName of the parent targ is equal in name and type (===) to INPUT, and parent targ is equal to ‘comoConheceu’
if (targ.nodeName === ‘INPUT’ && targ.name === ‘comoConheceu’) {

In other words, Do Something when the guy getting the event is
<input … name=“comoConheceu” />
(since you don’t want to affect your disabled input unless comoConheceu is the one getting the event… the others, we ignore).

    //the txtOutro field disabled attribute will be ... ahh.. HELP.
    this.elements.txtOutro.disabled = (targ.id !== 'outro');
} 

Yeah this one got me too, I’m confuzled. though if (targ.id !== ‘outro’) is false, then it looks like your orig JS: blah.disabled = false. Maybe it’s an extra-secure false? : )

At this moment, and just grabbing the code provided:
I’m having a “form is null” and it seems to occur on this line:
form.onclick = function (evt)

I think it’s because of the var in the JS:
//the var declaration seems correct and this element ID exists.
var form = document.getElementById(‘candidaturaOnline’);

Really? We can’t see that in your pastebin (or am I missing it?): I see an id on the fieldset, but I didn’t actually see the form tag itself… does the form have the id of “candidaturaOnline”?

<fieldset id=“comoConheceu”> is what I saw.

So, no more load of js script on the head section of the page?

Putting the JS (or better yet, just a referencing <script src=“thescripturl”></script> tag) right before the closing body tag means it’s certain to be working on HTML that has loaded. So it only needs to wait for user interaction with the form, and doesn’t have to check that the page has loaded (you could still reference the script in the head with src=“thescripturl” but then you will need a load listener to wait til the page has loaded before running the script).

That used to be the case due to web browser before HTML 4 only being capable of loading from the head.

There are a couple of problems with loading from the head:

[list][]Loading of the page is permanently delayed until head scripts are downloaded, and run to completion
[
]Unable to directly work with page elements
[*]When using onload events to work with page elements later, complexity of andling multiple load events[/list]

As a result placing scripts just before the </body> tag solves multiple problems:

[list][]Pages appear to load faster
[
]You have direct access to DOM elements that occur before the script
[*]Scripts run earlier than onload, and even earlier than ondomready events[/list]

That’s likely to be due to the earlier line starting with “var form …”
I made the assumption that your form has an id attribute on it called “candidaturaOnline”


<form id="candidaturaOnline" method="..." action="...">

Okay, let’s work through the comments.


//the var declaration seems correct and this element ID exists.
var form = document.getElementById('candidaturaOnline');

The ‘candidaturaOnline’ id is expected to be the one on the form tag. This particular example doesn’t require it to be the form, itcould just be a common element that contains all of the radio buttons, but it’s very useful to have the variable called form referencing the actual form.


//Here we are attributing a function (that accepts one argument) to a specific event on our form. Is this precise?
form.onclick = function (evt) 
{

This is precise. Whenever an onclick event is triggered on the page, and that event bubbles up to the form element, that event triggers the function. For events that are triggered, the web browser automatically passes the event object as the first argument of the function.


...
    //what does it do here? Equals the evt var to himself and to a window.event, but, what is the propose?
    evt = evt || window.event;

The event object from above that’s passed to the function, that occurs on all web browsers except for Internet Explorer. The cross browser solution is to assign the evt argument if it is truthy, but if its value is falsy to assign the window.event object instead.

This page at the Quirksmode web site provides a good intro to events, browser compatibility problems, and ways to deal with them.


...
    //and here? Are we giving two values to one variable?
    var targ = evt.srcElement || evt.target;

This is similar to the “rest of the world” plus internet Explorer situation. The event object of all other web browsers use the srcElement property to refer to the element that was clicked. Internet Explorer uses the target property instead, to refer to the element that was clicked on. The event properties page provides good details on this, and also has an earlier version of code to find the target of an event.

As a bit of background, when using the || (OR) operator, javascript checks the truthiness of each term. As soon as javascript comes across one that is truthy, it returns true.

So, if evt.srcElement exists, then that is assigned to the targ variable. If evt.srcElement does not exist, we assign instead the evt.target object.

It’s much more stable to use object detection instead of browser detection, because sometimes browsers improve their capabilities, or other web browsers may have similar problems, such as Opera. By checking if objects and capabilities exist, we can be sure to successfully handle a much larger range of situations.


...
    //I have seen some nodeTypes but no numeric values attached,
    //does this means select "Radio Buttons" ?
 
    //if the target grabed by the even handler is 3... (precise?)
    if (targ.nodeType === 3) {

The above accounts for a quirk of Safari web browsers, where it assigns the text node that was clicked as the target. In all other web browsers, when you click on some text, it is the containing element that is the target. The values for Node types can be referenced by Node.ELEMENT_NODE and Node.TEXT_NODE but Internet Explorer once again is the odd duck out and halts execution because it doesn’t know what they mean. As such it’s more compatible to use the numeric values instead, 3 for a text node and 1 for an element node.


...
        //redeclare targ to have the parentNode allocated. (precise?)
        targ = targ.parentNode;
    }

If Safari does give us a text node, we walk up the DOM to its parent, which gives us the element node, so that now Safari browsers behave the same for our script as all other browsers.


...
   //if nodeName of the parent targ is equal in name and type (===) to INPUT, and parent targ is equal to 'comoConheceu'
    if (targ.nodeName === 'INPUT' && targ.name === 'comoConheceu') {

The above part limits us to only the radio buttons that we’re interested in

        
...
        //the txtOutro field disabled attribute will be ... ahh.. HELP. 
        this.elements.txtOutro.disabled = (targ.id !== 'outro');
    }
}

When the radio button (targ.id) is not the ‘outro’ one, we want the txtOutro to be disabled.

Perhaps a clearer way to have coded that is to first work out if we’re on the ‘outro’ radio button, and then to disable when we’re not on it.

        
...
        //the txtOutro field disabled attribute will be ... ahh.. HELP. 
        var isOutro = (targ.id === 'outro')
        this.elements.txtOutro.disabled = !isOutro;
    }
}

I think a couple of different things may be getting confused here.

Yes, W3C browsers do retain the whitespace within the DOM, but they don’t play a part when targeting events.

My understanding is that in W3C browsers, events are always associated with the element, not the text. Safari, which is the lone-wolf here, associates the text node with the event instead.

I think that the rewrite in my reply helps to clear things up. When the radio button is not the ‘outro’ button, we want to disable the outro text field. When it is the ‘outro’ radio button, we want to enable the outro text field.

The reversal of nots in the original code does get confusing, so splitting it up with a suitable variable name can help to make it easier to understand.


var isOutro = (targ.id === 'outro');
this.elements.txtOutro.disabled = !isOutro;

Yeah, I was making an assumption based on his script which was:


document.forms.candidaturaOnline ...

Are you staying here for a while? :slight_smile: Like, a long while? :smiley:

Hope so cas, with this quality answers I’m sure I will learn a lot.

Thanks a lot for the details on this answers, I love answers that, in order to reply, I have to properly study them. And it’s exactly what I will do.

I do generally understand the concepts behind your answers, but I will need some time to write it all down and see if I truly get it.

Reply in a few days,
Márcio

Oh, Paul’s not planning to leave any time soon (right?) : ) Anyway, he’d better not, I haven’t learned enough yet!

Never fear, I plan to stick around for quite a while yet.