Form submits before I can change HTML

Viewed 96

I'm trying to stop a form from automatically submitting, then use JavaScript to replace the form with text telling the user the server is processing the form, and in the callback in my 'post' route on the server the user gets sent to the results page after some processing finishes with information from the form. But before the span can appear in the HTML the form has already been submitted and the page is waiting for a response from the server. If I insert a timeout function I can force the span element to appear before the form gets submitted, but there's got to be a better way to do this. I tried using async await .then() but that also didn't work - maybe loading the span can't finish rendering before the form is submitted? - Thanks

HTML and JS:

var proc = async () => {
    var form = document.getElementById('form');
    form.style.display = 'none';
    var span = document.getElementById('testSpan');
    span.style.display = 'block';

    //using this timeout I can get the span element to appear
    //window.setTimeout(() => {
        //document.getElementById('form').submit()
    //}, 500);
}

var btn = document.getElementById('submitBTN');

btn.addEventListener('click', async () => {
    await proc().then(document.getElementById('form').submit());
})
<span style='display : none' id="testSpan">processing...</span>
<form method="POST" action="/submit-form" id="form" onsubmit="event.preventDefault();">
    <input required id="linkInput" type="text" name="name" />
    <input id='submitBTN' type="button" value="Submit"/>
</form>

2 Answers

If you want to continue processing after posting to server and before showing its resulting page, Ajax is the easiest way to go. Normally, people rather preprocess before posting and then let the server init a new page almost right away.

Ether way, in the interval, to render any new showing during a running process or preprocess, you must make that running stop and go. The easiest way to do so is with the trick of the setTimeout of 0 ms: you modify what you want to show, do a setTimeout of the remaining process and end that main process; a bit like:

// Change to display
form.style.display = 'none';
span.style.display = 'block';
// Put the remain of process as an after rendering
setTimeout( () => {
  // remaining process
}, 0 );
// End of main process, so the browser renders

If you want to render many times during the process, I would refer you to a more complete answer here.

Using async and await does not make things into promises. You are going to have to return a promise to use a promise.

In your cause, you are still going to have to use a timeout. In the end it is not really different than you just calling the submit in the timeout.

var proc = async() => {
  document.body.classList.add("submitting");
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, 500);
  });
}

var btn = document.getElementById('submitBTN');
btn.addEventListener('click', () => {
  proc().then(() => document.getElementById('form').submit());
})
#testSpan,
.submitting form {
  display: none;
}


.submitting #testSpan {
  display: block;
}
<span id="testSpan">processing...</span>
<form method="POST" action="/submit-form" id="form" onsubmit="event.preventDefault();">
  <input required id="linkInput" type="text" name="name" />
  <input id='submitBTN' type="button" value="Submit" />
</form>

Related