Occasionally I encounter a problem which seems familiar, that I recognize, but don’t immediately know the cause. The solution still takes time and I often take the same steps and follow the same processes used previously. Today I re-encountered an error caused by the default submit event handler cancelling a download.

Today we encountered some interesting behaviour on mfmdatahub.io. On the scenario results page there is a drop-down box which allows users to choose a file to download. It was working intermittently, sometimes the file would download but more often than not the page would reload. The behaviour was the same for multiple browsers.

The website is designed to (inelegantly) reload whenever it receives an 401 unauthorized response from the backend – so we initially suspected that there was a “permissions issue” and we would need to check the user’s access rights. Using Firefox’s Developer Tools we noticed that the requests were being cancelled with the error NS_BINDING_ABORTED before the page was reloaded. We suspected an incorrect CORS response header, or an SSL error preventing access to the URL (the file was hosted on a different domain).

Using a debugger to step through the Javascript execution of a bundled React.JS application was difficult. Even with the built-in source code beautifier it is difficult to follow short variable names and optimised control flow. Surprisingly the Javascript bundle did contain few but identifiable symbols from our codebase. Eventually I used the debugger to add a breakpoint on all XHR requests and DOM events and found a familiar problem.

In this case we had a <form> element wrapping a React.JS <Button/> component. The button had a onClick handler.

<form role='form' className='form-inline'>
<Button
    text='Download'
    disabled={!this.state.selectedCCTVRecording}
    className='btn btn-primary'
    onClick={this.handleDownload}
    data-toggle={!this.state.appToken ? 'No token' : ''}
    disabled={!this.state.appToken}
/>
</form>

I don’t know why both React.JS form components are used with vanilla HTML tags. I expect it was due to the implementation of accessibility standards that required the role='form' mark-up. Looking at the git commit history the <form/> element was added after the <Button/>.

When there is a button within a form two DOM events are published. There will be a “click” event for the Button, and a “submit” event for the Form. The download is started by the “handleDownload” event handler, but the default event handler for the “submit” reloads the page, cancelling the download requests. The default action of a HTML form is load the current page and append the form values as URL query parameters.

A quick fix was required, so I added an event handler to the React.JS component which called event.preventDefault() which halts the event processing.

preventDefaultFormSubmit = (event) => {
      event.preventDefault();
    }

I could have spent longer re-writing the component to use React.JS forms – but I opted to add a “submit” handler in the form element:

<form role='form' className='form-inline' onSubmit={this.preventDefaultFormSubmit}>