Search code examples
javascriptecmascript-6formsfetch-api

Fetch API - Targeting an Iframe


Using the target attribute of an html form, you can post form data and then have the html-server-response showcased in an iframe.

form.setAttribute("target", "nameOfIframe");

Can the same thing be achieved posting with the ecmascript fetch api (without creating an actual html form element)?


Solution

  • I was able to get the fetch POST response into the iframe by using the srcdoc property of the iframe, as advised to me by this AI answer.

    async function sendFetchResponseToIframe()
    {
      const formData = new FormData();
      formData.append('input1', 'value1');
      formData.append('input2', 'value2');
      
      let options = {};
      options.method = 'POST';
      options.mode = 'cors';
      options.body = formData;
      
      let postURL = 'http://neartalk.com/test/formData.php';
    
      let response = await fetch(postURL, options);
      let data = await response.text();
    
      let iframe = document.getElementById("frame1");
      iframe.srcdoc = data;
      
    }
    sendFetchResponseToIframe();
    <p>The fieldset below contains an iframe that gets updated to the Fetch POST response:</p>
    <fieldset>
      <legend>Iframe</legend>
      <iframe id="frame1" style="width:100%" src="http://www.example.com/" frameborder="0"></iframe>
    </fieldset>

    However, this method is not as transparent as setting the target of an HTML form, which preserves all relative links that may be returned by a server's HTML response.

    While there are methods of addressing this, like: (1) Modifying the html response to include a base element, or (2) modifying the HTML response to convert all relative links to absolute links. I can still imagine other complications like server-side redirects that could ultimately occur. Such redirects would lead to you ultimately having to also modifying your fetch code to follow the redirects, determine the final URL, and convert the base URL or absolute links accordingly.

    Due to these complications, the answer to my question is ultimately: No; you're better off creating a hidden HTML form dynamically, and setting its target prior to submission, and then ultimately removing that dynamically created form after submission. This method more easily protects the relative links that could potentially be in the HTML response:

    function submitFormToIframe(actionURL, formData, iframe)
    {
      const form = document.createElement('form');
      form.action = actionURL;
      form.method = 'POST';
      form.target = iframe.name;
      for (const [name, value] of formData.entries())
      {
        const input = document.createElement('input');
        input.type = 'hidden';
        input.name = name;
        input.value = value;
        form.appendChild(input);
      }
      form.style.display = "none";
      document.body.append(form);
      form.submit();
      document.body.removeChild(form);
    }
    
    async function main()
    {
      // Create Form Data:
      const formData = new FormData();
      formData.append('input1', 'value1');
      formData.append('input2', 'value2');
      
      // Get iframe from DOM:
      let iframe = document.getElementById("frame1");
      
      let actionURL = 'http://neartalk.com/test/formData.php';
      submitFormToIframe(actionURL, formData, iframe);  
    }
    onload = main;
    <p>The fieldset below contains an iframe that gets updated via generated form submission:</p>
    <fieldset>
      <legend>Iframe</legend>
      <iframe id="frame1" name="frame1" style="width:100%" src="http://www.example.com/" frameborder="0"></iframe>
    </fieldset>

    Due to these findings, I think the specification writers, should consider adding a fetch option that allows you to send a fetch response directly to a specified iframe, in a manner where relative links aren't susceptible to breakage (as is the case with HTMLFormElement.target. This would make the fetch API a more attractive solution.