I'm using an empty frameto handle pseudo-asynchronous form submission. For those that aren't familiar with the technique, the idea is to reference the frame's name
attribute in the form's target
attribute, such that the URI of the form's action
resolves in the frame and the user experience isn't interrupted.
To feedback to the user I need to use script to listen for the load
or error
events on the form, and because this effectively makes the whole UX script-dependent, I'm only injecting the frame and adding the form's target reference via script.
The problem is that the frame will trigger a load event as soon as it's injected into the page: default behaviour.
I can mitigate this either with jQuery's one
method and e.stopImmediatePropagation()
(here), or I can inject the frame and then bind the events (here). But both of these options are really counter-intuitive — the code certainly needs commenting to avoid looking like a mistake.
TL;DR: Is there any way an iframe can be modified or qualified to stop it firing an erroneous load event on injection in the first place?
What I've tried
src
attributesrc
set to about:blank
(which the above is implicit for)src
set to #
src
set to javascript:void 0
src
set to empty data-URI data:text/plain;base64,;
srcdoc
set to empty or near-empty stringThis isn't possible, as you stated, the default src
is set to about:blank
and any other value will fire either the load
event or the error
one.
So the cleanest solution might be to start listening to these events only when the submit event of the form is fired:
const frame = document.createElement("iframe");
frame.name = "frame";
document.body.append(frame);
const form = document.querySelector("form");
form.addEventListener("submit", (evt) => {
const controller = new AbortController();
const { signal } = controller;
const handler = (evt) => {
console.log("Request", evt.type === "error" ? "failure" : "success");
controller.abort(); // kill us and the other listener
};
frame.addEventListener("load", handler, { signal });
frame.addEventListener("error", handler, { signal });
});
<form
target=frame
method=post
action="https://stacksnippets.net/js">
<button>post it</button>
<textarea name=html>Some content</textarea>
</form>
But it should probably be noted that for the error event to fire you'd need to have a response that the browser can't open in an iframe, and browsers can open a lot of stuff in an iframe, and moreover, most servers do send proper HTML pages when they face an error. So in most cases this won't let you know that the request failed, even a 404 error will fire the iframe's load event.
So the best, if you're in a same-origin situation, might be to use AJAX to make the request. From there you can check the response's status code, and even make the server give you useful information e.g as a JSON response.
You can then populate the srcdoc
of your iframe with the response consumed as text. (External fiddle since StackSnippet's null-origined frames can't make same-origin requests).