iframe 내에서 alert, confirm, prompt 띄울 경우 오류가 발생
오류 내용:
A different origin subframe tried to create a JavaScript dialog. This is no longer allowed and was blocked. See https://www.chromestatus.com/feature/5148698084376576 for more details.
현재 크롬 같은 버전이라도 서브 버전에 따라 누구는 재현되고 안되고 할 수 있음
하지만 기능 제거는 확정된 사안인 만큼 가급적 선행 조치 필요함
해결 방법
1. postMessage로 자식 페이지에서 띄울 메시지를 부모 페이지로 전달해서 alert으로 띄우기
2. 기본 네이티브 alert 대신 디자인 모달로 변경해서 띄우기
1번이 좋을 듯
2번은 아이프레임이라 모달 창 이슈 더 많을 것 같음
iframe인 경우 자식 페이지에서 띄울 메시지를 부모 페이지로 전달해서 alert으로 띄우는 방식 사용
참고 코드
자식 페이지 (iframe)
iframe으로 들어간 경우 postMessage로 부모 페이지에 메시지 전달 후 부모 페이지에서 alert으로 띄움
alert("이거 뿌려 주세요."); -> customAlert("이거 뿌려 주세요.");
function customAlert(message) {
if (window.self !== window.top) {
console.log('iframe loaded');
window.parent.postMessage({
action: "systemAlert",
message: message
}, "*");
} else {
alert(message);
}
}
부모 페이지
전달받은 메시지를 부모 페이지에서 다시 alert으로 띄우기
function systemAlert(message) {
alert(message);
}
window.addEventListener("message", (event) => {
// 메시지의 오리진을 체크하여 신뢰할 수 있는 메시지인지를 확인
// if (event.origin !== "http://localhost:3000") {
// return;
// }
var data = event.data;
if (typeof(window[data.action]) == "function") {
window[data.action].call(null, data.message);
}
}, false);
Override alert
자바스크립트 alert 함수 오버라이드(override)하여 사용하기
window.alert = (function (original) {
return function (msg) {
if (window.self !== window.top) {
window.parent.postMessage({
action: "systemAlert",
message: msg
}, "*");
} else {
original(msg);
}
}
})(window.alert);
confirm(), prompt()
confirm, prompt의 경우 부모 페이지에서 confirm, prompt 창을 띄우고 입력된 값을 다시 iframe으로 보내야 하기 때문에 약간 까다롭습니다.
구글은 다음에 또 무엇을 할까요..? 이제 window.postMessage()를 비활성화시킬까요?
개발자들은 이것 저것 테스트해보느라 여기에 많은 시간을 투입하게 되네요. 다시 Internet Explorer 시대로 돌아가는 게 아닌지..
parent.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Parent (domain A)</title>
<script type="text/javascript" src="https://example.com/dialogs.js"></script>
</head>
<body>
<h1>Parent (domain A)</h1>
<iframe src="https://example-b.com/iframe.html">
</body>
</html>
iframe.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Iframe (domain B)</title>
<script type="text/javascript" src="https://example.com/dialogs.js"></script>
</head>
<body>
<h1>Iframe (domain B)</h1>
<script type="text/javascript">
alert('alert() forwarded from iframe.html');
confirm('confirm() forwarded from iframe.html via callback', (result) => {
console.log('confirm() result via callback: ', result);
});
prompt('prompt() forwarded from iframe.html via callback', null, (result) => {
console.log('prompt() result via callback: ', result);
});
(async () => {
var result1 = await confirm('confirm() forwarded from iframe.html via promise');
console.log('confirm() result via promise: ', result1);
var result2 = await prompt('prompt() forwarded from iframe.html via promise');
console.log('prompt() result via promise: ', result2);
})();
</script>
</body>
</html>
dialogs.js
(function() {
var id = 1,
store = {},
isIframe = (window === window.parent || window.opener) ? false : true;
// Send message
var sendMessage = function(windowToSend, data) {
windowToSend.postMessage(JSON.stringify(data), '*');
};
// Helper for overridden confirm() and prompt()
var processInteractiveDialog = function(data, callback) {
sendMessage(parent, data);
if (callback)
store[data.id] = callback;
else
return new Promise(resolve => { store[data.id] = resolve; })
};
// Override native dialog functions
if (isIframe) {
// alert()
window.alert = function(message) {
var data = { event : 'dialog', type : 'alert', message : message };
sendMessage(parent, data);
};
// confirm()
window.confirm = function(message, callback) {
var data = { event : 'dialog', type : 'confirm', id : id++, message : message };
return processInteractiveDialog(data, callback);
};
// prompt()
window.prompt = function(message, value, callback) {
var data = { event : 'dialog', type : 'prompt', id : id++, message : message, value : value || '' };
return processInteractiveDialog(data, callback);
};
}
// Listen to messages
window.addEventListener('message', function(event) {
try {
var data = JSON.parse(event.data);
}
catch (error) {
return;
}
if (!data || typeof data != 'object')
return;
if (data.event != 'dialog' || !data.type)
return;
// Initial message from iframe to parent
if (!isIframe) {
// alert()
if (data.type == 'alert')
alert(data.message)
// confirm()
else if (data.type == 'confirm') {
var data = { event : 'dialog', type : 'confirm', id : data.id, result : confirm(data.message) };
sendMessage(event.source, data);
}
// prompt()
else if (data.type == 'prompt') {
var data = { event : 'dialog', type : 'prompt', id : data.id, result : prompt(data.message, data.value) };
sendMessage(event.source, data);
}
}
// Response message from parent to iframe
else {
// confirm()
if (data.type == 'confirm') {
store[data.id](data.result);
delete store[data.id];
}
// prompt()
else if (data.type == 'prompt') {
store[data.id](data.result);
delete store[data.id];
}
}
}, false);
})();
관련 논의
'Javascript' 카테고리의 다른 글
video loadmetadata 이벤트가 일관되게 실행되지 않는 이유 (1) | 2022.02.08 |
---|---|
자바스크립트 ??, !! 연산자 (0) | 2022.01.10 |
jQuery element에 어떤 이벤트가 있는지 확인하는 방법 (0) | 2021.04.26 |
javascript 특정 요소가 있는지 확인하기 (0) | 2021.04.22 |
jQuery 커스텀 데이터 셀렉터 (0) | 2021.04.20 |
개의 댓글