I’m debugging an issue and found strange behavior in how Firefox and Safari deal with the onclick JavaScript.
Here is my code:
<form id="createMessageForm" action="onclick.php?refresh=<?php echo ++$_REQUEST['refresh'];?>" method="post" >
<input type="hidden" id="iteration" value="1"/>
</form>
<a href="page2.html" target="_blank" onclick="document.getElementById('createMessageForm').submit();return true;">Hello</a>
Loading this in Firefox, it always opens ‘page2’ for me. But loading this in Safari, it only open the ‘page2’ for me about 1 in 10 times, or less. It’s like depends on whether the browser can finish the ‘return true;’ statement before the form got submitted or not.
I thought JavaScript is always single-threaded, so what’s the expected behavior in such code?
Javascript is single-threaded as you say. However it is also happy to work asynchronously.
HTTP requests are async events in javascript because they can take an unknown amount of time to complete. Javascript makes the call to the HTTP request and then carries on with whatever else it has to do until the HTTP request completes and returns control back to Javascript (or in this case, completes and triggers the browser to load the new page).
In your case, you’re basically firing two HTTP requests from Javascript, one right after the other. The second is fired because Javascript doesn’t wait for the first to respond; it just carries on to the next one. The unpredictable results you’re seeing are basically the result of whichever of the two HTTP requests completes its response first, to the point of getting the browser to load the new page.
This is also how AJAX works — the ‘A’ in AJAX stands for Asynchronous. Javascript makes the HTTP request, and will happily carry on doing whatever else you want of it. The HTTP request is handled by the browser, which triggers an event for Javascript to pick up when the request is ready. That’s why with AJAX calls you need to give it a ‘success’ function, rather than just saying
if(success)in the next line of code.And that leads me to a suggestion to solve your problem. Fire the form submission as an AJAX event. That way, the browser will never try to load it as a new page when it responds. You can still tell the browser to trigger the separate page load immediately afterwards if you like, although I would suggest waiting for the first call to finish, as otherwise you won’t know whether it has actually succeeded.
Hope that gives you some help.