Fetch API - 定位 Iframe

问题描述 投票:0回答:3

使用 html 表单的 target 属性,您可以发布表单数据,然后在 iframe 中展示 html-server-response。

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

使用 ecmascript fetch api 可以实现同样的功能(无需创建实际的 html 表单元素)吗?

javascript ecmascript-6 forms fetch-api
3个回答
2
投票

使用 JavaScript/EcmaScript 更新 IFRAME 的内容相当简单,可以使用

srcdoc
属性来完成。

document.getElementById('fetchTarget').srcdoc = `<!DOCTYPE html><p>Hello World!</p>`;
<iframe id="fetchTarget">
</iframe>

您需要做的就是安排更新它,作为处理对

fetch
:

的调用的一部分

fetch(`https://jsonplaceholder.typicode.com/posts/1`)
  .then(response => {
    console.log(response);
    if (response.ok){
      return response.text();
    }
  })
  .then(text => {
    document.getElementById('fetchTarget').srcdoc = text;  
  });
<iframe id="fetchTarget">
</iframe>

这个 URL 并不是一个特别好的示例,因为它返回 JSON 而不是 HTML,但它并不会真正影响结果。


1
投票

我能够使用 iframe 的 srcdoc 属性将 fetch POST 响应获取到 iframe 中,正如这 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>

但是,此方法不像设置 HTML 表单的目标那么透明,后者保留服务器的 HTML 响应可能返回的所有相对链接。

虽然有解决此问题的方法,例如:(1) 修改 html 响应以包含 base 元素,或 (2) 修改 HTML 响应以将所有相对链接转换为绝对链接。我仍然可以想象最终可能发生的其他复杂情况,例如服务器端重定向。此类重定向将导致您最终还必须修改获取代码以遵循重定向、确定最终 URL,并相应地转换基本 URL 或绝对链接。

由于这些复杂性,我的问题的答案最终是:不;您最好动态创建一个隐藏的 HTML 表单,并在提交之前设置其目标,然后最终在提交后删除该动态创建的表单。此方法更容易保护 HTML 响应中可能存在的相关链接:

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>

由于这些发现,我认为规范编写者应该考虑添加一个获取选项,允许您以相对链接不易损坏的方式直接向指定的 iframe 发送获取响应(就像以下情况一样) HTMLFormElement.target。这将使 fetch API 成为更具吸引力的解决方案。


0
投票

如果需要处理iframe内部的数据,可以使用

window.postMessage()
。当然,您可以将代码放在单独的文件中,并通过
iframe.srcdoc
引用它,而不是
iframe.src

属性

let btn = document.getElementById('btn')
let otherBtn = document.getElementById('btn2')
let wrongBtn = document.getElementById('btn3')
let results = document.getElementById('results')
function showResults(json) {
 results.contentWindow.postMessage({
  action: 'showResults',
  data: json,
}, "*");
}
function sendForm() {
  fetch('https://dummyjson.com/products/1')
    .then(res => res.json())
    .then(showResults)
}
function addLinkToIframe() {
  results.contentWindow.postMessage({
    action: 'addLink',
    data: {
      href: 'index.html',
      text: 'Click me!',
    },
  }, "*");
}
function addCallNonExistentAction() {
  results.contentWindow.postMessage({
    action: 'gibberish',
    data: 'even more gibberish',
  }, "*");
}
btn.addEventListener('click', sendForm)
otherBtn.addEventListener('click', addLinkToIframe)
wrongBtn.addEventListener('click', addCallNonExistentAction)

window.addEventListener('message', (event) => {
      if (event.data.action === 'iframe::DOMContentLoaded') {
        console.log('iframe loaded');
        results.contentWindow.postMessage({
          action: 'setBase',
          data: document.baseURI,
        }, "*");
      } else {
        alert(`Action '${event.data.action}' not defined`)
      }
})
<button id="btn">Send</button>
<button id="btn2">Add link</button>
<button id="btn3">Call non-existent action</button>
<hr />
<iframe id="results" srcdoc="<!DOCTYPE html>
<html>
<head>
  <base href=>
  <script>
    window.addEventListener('message', (event) => {
      if (event.data.action === 'showResults') {
        document.body.querySelector('pre').appendChild(
          document.createTextNode(JSON.stringify(event.data.data, true, '\t'))
        );
      } else if (event.data.action === 'setBase') {
        console.log('setbase')
        document.head.querySelector('base').href = event.data.data
        console.log(document.baseURI)
      } else if (event.data.action === 'addLink') {
        link = Object.assign(document.createElement('a'), {
          href: event.data.data.href,
        })
        link.textContent = event.data.data.text
        console.log('add', link)
        document.body.prepend(link);
      } else {
        alert(`Action '${event.data.action}' not defined`)
      }
    })
    window.parent.postMessage({
      action: 'iframe::DOMContentLoaded',
    }, '*')
  </script>
</head>
<body>
  Waiting ...
  <pre></pre>
</body>
</html>
"></iframe>

© www.soinside.com 2019 - 2024. All rights reserved.