PDA

View Full Version : Ajax+element.focus()



djr33
04-20-2013, 06:39 PM
I'm not quite ready to have this link indexed by Google, so I'll put it here at text:
ouraccent.com

The code is a mess at this point, so I don't expect you to go through it, and I hope that I can explain what's going on.

I'm using the Dynamic Drive Dynamic Ajax Content Script (http://www.dynamicdrive.com/dynamicindex17/ajaxcontent.htm), which brings in external content and loads it in a div. (This div also happens to be hidden then shown when the content loads.)

Then there's a text box in that newly loaded content, and I'd like to focus() it so that the cursor is ready to start typing in the text box.

I imagined there might be some issues with timing (whether it's ready or not), but that's not the issue. In fact, it doesn't seem to work when I run it from the console either.

The code I'm trying to use is:
document.getElementById('newurl').focus();

To try this out on the page, click on the "new" icon, which is located in the bottom left. In theory, the large text box should be focused when loaded. [By the way, if you're just testing it, please don't save anything on the website, since it is live, but you can just click anywhere outside that div to make it disappear.]

If you open your error console, you'll see the error, or you can then try the code (above) manually. The response I get (in Firefox) is:
Error: TypeError: document.getElementById(...) is null



Any ideas or workarounds?

traq
04-20-2013, 09:55 PM
It works when I run it from the console.

I do get an error about trying to focus "null" when I load the new window, though, so I'd say maybe it is a timing issue. I didn't have a chance to look at your actual code but I will when I get home.

djr33
04-20-2013, 10:04 PM
It works when I run it from the console.Really? I can't get it to work at all in Firefox. Otherwise, I'd think it's a timing issue. But it seems like it's not loading properly, because it doesn't ever work (I tried a timeout as well).


I do get an error about trying to focus "null" when I load the new window, though, so I'd say maybe it is a timing issue. I didn't have a chance to look at your actual code but I will when I get home. Thanks! I'd hope someone just happens to know what's wrong (I'm guessing something about Ajax-loaded content).

If you do look at the code, check line 222 of "map.js". (It's a long messy .js file, but that'll point you in the right direction.)



Edit: it does seem to work from the console in Safari, but not Firefox. I wonder if something else is wrong, perhaps with how the code is loading or whether there's something about the code that is causing a bug in FF.

Edit 2: though the code is still messy, I validated the page, including the resulting page after the Ajax occurs; there are no more errors or warnings. I also changed the doctype to HTML 4.01 strict, instead of XHTML (a relic of the older code). So it can't be that the HTML isn't valid. But it still might not be making the browser happy, somehow...

molendijk
04-20-2013, 11:06 PM
Daniel, I can't find an element having id="newurl" on your page. I think that's why the Error Console is complaining.

djr33
04-20-2013, 11:13 PM
You have to click on the "new" icon (it's white, in the lower left). Then it will be loaded with Ajax on top of the map. It's the large text box.

molendijk
04-20-2013, 11:33 PM
Yes, I did that. It made the console complain about the fact that document.getElementById('newurl') (line in map.js) does not reference an existing id on your page.

djr33
04-20-2013, 11:39 PM
It's not on the page until it loads, but then it is there. However, the browser doesn't seem to know it.

If I use "view selection source" to see what's happening on the page, I get the following code:

<p style="font-size:2em;margin-left:0px;margin-top:60px;text-align:center;">
Video URL on Youtube:
<br><input name="url" style="width:250px;" id="newurl" value="" onkeyup="if(this.value!='') { showel('savenew',0,1); } else (hideel('savenew',1));" type="text">
<br><input id="savenew" style="display:none;margin:auto;" value="Add to Map" onclick="editpin(document.getElementById('newurl').value); return false;" type="submit">
</p>
So it is definitely there, but the browser doesn't know it's there.

Is there any reason you know of that this might occur?



Edit: I keep re-double-checking to be sure there are no typos, and I can't find any. If I'm just missing something, that would explain it, so let me know if you see something. But as far as I can tell, it should be working.

traq
04-20-2013, 11:50 PM
I think it's just a timing issue.

You might consider adding a callback parameter to the ajaxpage function - something like
ajaxpage(
'/new.php'
,'userinfoajax'
,function(){
document.getElementById('newurl').focus();
}
);

I'm not sure how else you could check on the response status of the ajax request from outside the ajaxpage function.

(I still like jquery because of issues like this.)

molendijk
04-20-2013, 11:52 PM
If it's there after page load only, the line in map.js may not have recognized it early enough. You could try:

<script defer type="text/Javascript" src="/map.js">
/***********************************************
* Dynamic Ajax Content- Dynamic Drive DHTML code library (www.dynamicdrive.com)
* This notice MUST stay intact for legal use
* Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
***********************************************/
</script>

djr33
04-21-2013, 12:04 AM
If it's a timing issue, I can fix it as a timing issue. But I don't think it's entirely a timing issue-- any idea why in Firefox it is never recognized, even from the console? If I can get that fixed, then I'll worry about the timing (it may indeed be both).

molendijk
04-21-2013, 12:09 AM
That's strange. I get the error from the Firefox Error Console. I'm using FF 20.0.1.

molendijk
04-21-2013, 12:18 AM
I found this (http://www.dynamicdrive.com/forums/showthread.php?30748-SOLVED!-Focus-on-element-loaded-in-content-via-AJAX). Maybe it helps.

traq
04-21-2013, 12:24 AM
If it's a timing issue, I can fix it as a timing issue. But I don't think it's entirely a timing issue-- any idea why in Firefox it is never recognized, even from the console? If I can get that fixed, then I'll worry about the timing (it may indeed be both).

It works for me in firefox also (though you can't tell until you close the console). The firefox console clearly shows .focus() being called (and failing) before your ajax finishes loading the new content - I'm almost certain that that's where the problem is.

djr33
04-21-2013, 12:27 AM
Post 1:
Thanks for confirming that.

I thought I'd try to fix it just for Safari using a timeout (I thought I had done this before), but when I did that, now it works in Firefox too. Really odd.
And the weirdest part is that the console still doesn't work. Even when the timeout does.

My guess: the console (and other normal JS) operate based on the existing version of the page, which isn't updated using Ajax (for one reason or another). Then using a timeout is like eval(), and it does this at a superficial level so that any new code on the page is then seen as a fresh page, rather than using the stored version.


Anyway, this is fixed. I'm not sure it's perfect, but it works. And even if it doesn't happen to work (slow connection?) it won't be a big problem. The user can, easily, just click in the box.

I wish I knew a bit more about why Firefox is acting up, but I think I have some idea now.


Post 2:
I'd try that if it still wasn't working. I'm a bit hesitant to try the new code in case it might interfere with something else, and my guess (although I haven't tested it) is that it will have the same issue in Firefox. but thanks for the link. If I run into more trouble with this, I can try that out.

jscheuer1
04-22-2013, 02:44 AM
Well yes, if you focus on an element that isn't there yet and then inject new HTML into the page that contains that element, then the focus will not occur. Rather than a timeout though, I'd insert the new HTML and then focus on the element.

If that's what you are doing, than a 0 timeout usually is all that's required for the DOM to catch up.

Remember though, AJAX is asynchronous. Even if your focus command is after your AJAX command, that doesn't mean it happens after insertion of the AJAX result.

The only way to guarantee that is to have the same function that inserts the HTML (success in jQuery, onreadystatechange in regular javascript) set the focus, and only after the HTML is inserted, something like:


request.onreadystatechange = function(){
if(request.readyState == 4 && request.status == 200){
document.getElementById('targetdiv').innerHTML = request.responseText;
if(document.getElementById('newurl')){document.getElementById('newurl').focus();}
}
};

As I say, even if you do it like that, there might be a slight lag, though there shouldn't be, so if there is:


request.onreadystatechange = function(){
if(request.readyState == 4 && request.status == 200){
document.getElementById('targetdiv').innerHTML = request.responseText;
setTimeout(function(){if(document.getElementById('newurl')){document.getElementById('newurl').focus();}}, 0);
}
};

A 0 timeout should do the trick.

In the script you're using the onreadystatechange function is separate and is here:


function loadpage(page_request, containerid){
if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1))
document.getElementById(containerid).innerHTML=page_request.responseText

//modification: check again for audio players
//window.threeSixtyPlayer.init();

}

So that would have to be adapted (perhaps even having the page requested passed to it to be used as a test to see whether or not to bother looking for the newurl - that's only if other AJAX request to other target elements might be made) and I don't believe any timeout would be required. Using a 500ms timeout where you do have it now is no guarantee. If the request takes longer than 500ms, it will still fail to focus. So:


function loadpage(page_request, containerid){
if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1)){
document.getElementById(containerid).innerHTML=page_request.responseText
if(document.getElementById('newurl')){
document.getElementById('newurl').focus();
}
//modification: check again for audio players
//window.threeSixtyPlayer.init();
}
}

should do it as long as there is only one target element for any and all AJAX requests on that page. And you can get rid of:


setTimeout(function(){document.getElementById('newurl').focus()},500);

djr33
04-22-2013, 04:38 AM
Thanks, John! I'll implement that when I get a little time.

Do you understand what was wrong with Firefox in the first place, though?

At this point my guess is that all browsers were having issues because of timing, but then that caused Firefox to give up later, so that it didn't work from the console either. Some sort of "if it fails once" rule.

Assuming that's the case, then this solution should be fine. But I would like to be confident that nothing else is wrong.

jscheuer1
04-22-2013, 05:16 AM
I really wasn't following the thread that closely. I was busy and wanted to see what was going on. By the time I actually got to it, things had gone on for awhile. So I'm not really clear on what the exact issue with the Fox was.

But it's true that sometimes the console will not give you all of the most accurate information, and the various tools like DOM inspectors don't always update, it depends upon which you use. The DOM thing I have for Firefox usually updates, but not always, and if you refresh the page, you have to close the inspector and open it again to get anything.

I prefer Chrome's tools now, they're so well integrated. I'll use them to get something working, then just test to see if it works in others.

None of that has much to do with what I typed about AJAX, that's just how AJAX works. It fires off, waits for the response and then completes. In the meantime your other script code continues. If it needs something from AJAX it has to be part of the response function. AJAX can fail. so it's a good idea to catch the error and give the user some clue as to what went wrong and what they can do, like try again, or try again later. That DD script isn't setup real well for that. jQuery has a success and an error callback, so it's easy. With that DD script you would have to trap readyState 4 and any status code other than 200 to get a clue as to the problem and/or have somewhere to run onerror code.

But generally, if you're just fetching a page from the server, it will work most of the time, so it's just a matter of how thorough you want to be. If you were running a server side script though it might be more important to include error handling.

djr33
04-22-2013, 05:24 AM
Quick summary: focus() did not work within the function, but it also didn't work later such as from the console. This lead me to believe it was a fundamental problem in Firefox unrelated to timing. But then once I fixed the timing issue (with setTimeout), it seems to have fixed everything; I'm not sure why.

So as long as there aren't any remaining major issues (my impression is that there are not, but I'm not certain), then I think just fixing the timing is the right solution. Does that seem right?

jscheuer1
04-22-2013, 01:30 PM
I don't think so. The timeout only delays for 500ms. There's no guarantee that's always going to be long enough for the request to complete. I'm not sure why my additions to the loadpage function didn't work, I may have made a typo. Is it possible to setup a copy of the page using that?

The browser cache would have to have been cleared and the page refreshed to make sure it was working. And as I said a 0 timeout could be used:


function loadpage(page_request, containerid){
if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1)){
document.getElementById(containerid).innerHTML=page_request.responseText;
setTimeout(function(){
if(document.getElementById('newurl')){
document.getElementById('newurl').focus();
}
}, 0);
//modification: check again for audio players
//window.threeSixtyPlayer.init();
}
}

But really shouldn't be required. It just might though.

djr33
04-22-2013, 05:02 PM
Sorry, John! My post was ambiguous. I was just filling you in on the old information, unrelated to your updates. I'm sure those will work (and I'll post a link once I've included them). I'm a little busier this week, but I'll try to get back to it when I can.

My question was just about the old code and why it wasn't working. If it's only a timing issue, then fixing the timing issue is fine (and expected). If not, I'd like to know what else was going on (for example, I spent maybe an hour going through the code just looking for typos, imagining that the code wasn't properly recognized in Firefox to .focus() at all).

jscheuer1
04-22-2013, 07:13 PM
I see. Generally you will get an error somewhere. With something like that, where the page isn't being reloaded, I'd look in the error console, (perhaps called the javascript console) rather than any other console. It all depends upon what your add ons are and how familiar you are with the newer 'integrated' consoles Firefox now has though.

The fact that it would work in other browsers seems to indicate timing rather than syntax, but it's no guarantee.

If you want, put up the old version as a copy of the current page and I'll have a close look at it.

That said, with AJAX timing is always a potential issue. You cannot act upon the imported content until it arrives. So much so that if this:


ajaxpage('/new.php','userinfoajax');
showel('userinfo');
hideel('multiple');
document.getElementById('newimg').src="/ico/new_c.png";
document.getElementById('newurl').focus();

was what you were doing before, I'm a little surprised it worked in any browser. But some browsers will sometimes automatically focus on new content, that might be what was happening.

But then again, where is newurl? Is it in the imported content or was it already on the page? If I've misunderstood and it was already on the page, then there was some other problem. And what about newimg? Is that in the imported content, or was that already on the page? If it's not giving you any problems I would think it's already on the page. A quick look at new.php seems to indicate that newimg is not being imported and that newurl is and that newurl is a form input. That adds to the possibility that some browsers might focus on it automatically.

djr33
04-22-2013, 11:30 PM
...I'm a little surprised it worked in any browser.It didnt. But in Firefox it didn't work from the console either-- that is, the timing issue "broke" the page in Firefox so it never worked. (Or it was otherwise broken, then somehow fixing the timing fixed it.)

I think that all makes sense now.

(And yes, the element is within the Ajax content.)

jscheuer1
04-23-2013, 03:20 AM
Hmm, the console in Firefox. In older versions it used to execute anything upon the active window. In more recent versions, only upon the Firefox javascript engine. So you can do:


alert('here');

and that will work but not:


alert(document.body.innerHTML);

But that's just one of the consoles. I think there's now one available from:

Tools > Web Developer > Web Console

That's much more advanced. I like it, but am still getting used to it. It has tabs, that's the one I was talking about before. But it still doesn't execute anything, not even against the script engine. It does have a scratch pad feature though, and that will execute against the page (shift+F4) or:

Tools > Web Developer > Scratchpad

At least I think those are both now part of the new integrated stuff that comes with Firefox because I didn't add it, at least I don't think I did.

djr33
04-23-2013, 03:26 AM
Oh? How odd. I haven't needed to use the console much recently, I guess, but it did work in the past.

So in summary:
1) The console wasn't working for unrelated reasons.
2) There was a timing issue.

Now, it's all working. Ok, thanks!



Out of curiosity does the address bar have the same limitation? If I type in javascript:alert(document....) should that work?

jscheuer1
04-23-2013, 03:47 AM
It does (that is does not allow you to access the document in the window). I was really ticked off when I first noticed that and the bit about the console because at the time I was using Firefox as my primary method for testing/tweaking. I've since moved to Chrome and will probably not be coming back. But the Scratchpad feature is good for testing Firefox specific things, I'm glad I discovered that. If you have Firebug (the extension), its console is probably still capable of accessing the document, but I'm not sure, I never got into Firebug that much. It seemed too resource intensive to me.

djr33
04-23-2013, 03:57 AM
That's simply stupid, and might be the final straw for me with Firefox. I'm tired of discovering more "improvements" like this. Oddly enough I notice that a bookmarlet still works, but that's too much trouble to bother with. Perhaps I'll switch over to Chrome for testing too!

jscheuer1
04-23-2013, 04:15 AM
Right, bookmarklets still work. And right, that's too much trouble for something you just want to test quickly and probably not use again soon if at all.

But the Scratch Pad is cool and works well in the limited testing I did. And it allows you to save it so that you can keep the same stuff in it for a short or longer time if you want. I haven't tested that feature yet, not sure exactly how it would play out in 'real life' situations. Chrome's console allows you to navigate previous commands you typed in with the up and down arrow keys. They're not kept forever, but new instances inherit the old one's for a session at least. IE's allows you to use a single or multi line mode. In multi line mode the commands stay there until you remove them so you can easily try them in different modes and/or pages, not to mention after a refresh, or repeatedly on the same page.