使用 html 表单的 target 属性,您可以发布表单数据,然后在 iframe 中展示 html-server-response。
form.setAttribute("target", "nameOfIframe");
使用 ecmascript fetch api 可以实现同样的功能(无需创建实际的 html 表单元素)吗?
使用 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,但它并不会真正影响结果。
我能够使用 iframe 的 srcdoc 属性将 fetch POST 响应获取到 iframe 中,正如这 向我建议的那样。
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 成为更具吸引力的解决方案。
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>