[Web API] Window.open(), Window.opener

참고 문서

개요

인스턴스 메서드 Window.open()과 인스턴스 프로퍼티 Window.opener 정리

open()

window.open( URL, name [ , specs ] [ , replace ] )
  • URL: 문서의 URL
  • name: URL이 로드될 창의 이름을 지정하거나 기존의 창 이름을 지정할 경우 해당 윈도우를 재사용한다.
  • specs: 창의 위치와 크기, 기능 등 창이 갖는 특성을 지정한다. 이 값은 브라우저/버전마다 적용이 되기도 하고 무시되기도 한다. 가령 location은 과거엔(IE 구버전에선 아직도 적용됨) 숨길 수 있었으나, 최근엔 대부분의 브라우저에서 보안상의 이유로 무시하는 값이다.
  • replace: true일 땐 새 문서가 이전의 문서와 교체된다. false이거나 지정되지 않으면 새 문서는 창의 브라우징 히스토리에 새 항목으로 추가된다.
function openNewWindow(url, name) {
  var specs = "left=10,top=10,width=372,height=466";
  specs += ",menubar=no,toolbar=yes,location=no,status=yes,titlebar=no,scrollbars=yes,resizable=yes";
  window.open(url, name, specs);
}

name: 연결된 문서를 읽어 지정한 이름의 윈도우나 프레임(frame)을 target으로 설정한다. 지정된 이름을 가진 윈도우가 없으면 새 창으로 처리된다.

  • _blank: 연결된 문서를 읽어 새로운 빈 윈도우에 표시한다. 윈도우 이름은 없다.
  • _media: 연결된 문서를 읽어 미디어바의 HTML 내용부분에 표시한다. IE6부터 적용된다.
  • _parent: 연결된 문서를 읽어 바로 상위 모체창에 표시한다.
  • _search: 연결된 문서를 읽어 브라우저의 검색창에 표시한다. IE5부터 적용된다.
  • _self: 디폴트이며, 연결된 문서를 읽어 현재창에 표시한다.
  • _top: 연결된 문서를 읽어 최상위 윈도우에 표시한다.

URL과 name만 명시하면 대부분의 브라우저에서 새 탭으로 처리된다. 만약 단순 새 탭이나 새 창이 아닌 '팝업'이라 통용되는 창을 생성하려 한다면 사이즈(width, height)를 명시해야 한다:

window.open("/test.jsp", "팝업", "left=10, top=10, width=500, height=500");

화면 정중앙에 새 창을 띄우려면 다음처럼 부모창의 window 객체가 반환하는 사이즈 값을 이용해 처리한다.

var width=800, height=500;
var left = (screen.availWidth - width)/2;
var top = (screen.availHeight - height)/2;
var specs = "width=" + width;
specs += ",height=" + height;
specs += ",left=" + left;
specs += ",top=" + top;

window.open("/test.jsp", "팝업", specs);

브라우저별 특성

크롬

새 창 옵션 중 menubar가 yes일 경우 크롬은 새 창 팝업 대신 새 탭 팝업으로 열린다.

파이어폭스

TODO

엣지

TODO

opener

window.opener.document

window.open()으로 윈도우 객체가 생성될 때 자바스크립트는 window.opener 프로퍼티에 윈도우를 연 객체(부모)를 저장한다. 이를 이용하면 자식 창에서 부모 창을 컨트롤하거나 서로간 데이터를 주고받는게 가능하다.

example

간단한 예시를 통해 araboja:

test.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
  var newWindow;

  function openNewWindow() {
    newWindow = window.open("test2.html", "newWindow");
  }

  function recieve() {
    var txt = "<font color='red'>자식창에서 받아온 값</font>";
    document.getElementById("process").innerHTML = txt;
    document.myform.receiver.value = newWindow.document.myform.sender.value;
  }
</script>
</head>
<body>
<form name="myform">
  <input type="button" value="자식창 열기" onclick="openNewWindow()"><br>
  부모창 Sender : <input type="text" name="sender" size="10"><br>
  부모창 Receiver : <input type="text" name="receiver" size="10"><span id="process"></span><br/>
  <input type="button" value="받아오기" onclick="recieve()">
</form>
</body>
</html>

test2.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
  function recieve() {
    var txt = "<font color='red'>부모창에서 받아온 값</font>";
    document.getElementById("process").innerHTML = txt;
    document.myform.receiver.value = window.opener.document.myform.sender.value;
  }

  function send() {
    var txt = "<font color='blue'>자식창에서 보낸 값</font>";
    window.opener.document.myform.receiver.value = document.myform.sender.value;
    window.opener.document.getElementById("process").innerHTML = txt;
    window.close();
  }
</script>
</head>
<body>
<form name="myform">
  자식창 Sender : <input type="text" name="sender" size="10"><br>
  자식창 Receiver : <input type="text" name="receiver" size="10"><span id="process"></span><br/>
  <input type="button" value="받아오기" onclick="recieve()">
  <input type="button" value="부모창에 데이터 보내고 닫기" onclick="send()"/>
</form>
</body>
</html>

반대로 부모 창에서 자식 창의 문서에 접근할 땐 window.open() 함수가 반환하는 객체를 이용한다:

var winObj = window.open();

만약 부모 창과 데이터를 주고 받을 자식 창이 여러 페이지에서 공통으로 사용되는 경우(공통팝업)라면, 자식 창에서 부모창을 직접 조작하는것보다 부모창의 함수를 호출하여 파라미터로 던져주는 방식을 써야한다. (혹은 부모의 함수명을 콜백으로 전달하는 방식을 쓰던지..)

test3.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
  var newWindow;
  function openNewWindow() {
    newWindow = window.open("test4.html", "newWindow");
  }
  function sendMeData(data) {
    var txt = "<font color='red'>자식창에서 받아온 값 : </font>";
    document.getElementById("process").innerHTML = txt;
    document.getElementById("result").innerHTML = data;
  }
</script>
</head>
<body>
<input type="button" value="자식창 열기" onclick="openNewWindow()"><br>
<span id="process"></span><span id="result"></span>
</body>
</html>

test4.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
  function send() {
    var data = document.myform.sender.value;
    window.opener.sendMeData(data)
    window.close();
  }
</script>
</head>
<body>
<form name="myform">
  자식창 Sender : <input type="text" name="sender" size="10"><br>
  <input type="button" value="부모창에 데이터 보내고 닫기" onclick="send()"/>
</form>
</body>
</html>

window.opener에 권한 문제로 접근할 수 없는 경우 해결방법

opener.name;
"액세스가 거부되었습니다." // 익스(사망)
SecurityError: Blocked a frame with origin "http://www.daum.net" from accessing a cross-origin frame // 초로미
Error: Permission denied to access property 'name' // 불여시

window.open()으로 생성된 새 창(자식 창)에서 부모 창에 접근할 수 없는 경우가 있는데, 이것은 보통 부모 창과 자식 창의 호스트명이나 프로토콜, 포트가 다를때 동일 출처 정책(same-origin policy)에 따라 브라우저가 접근을 차단하는 경우다.

이를 해결하는 방법은 두 가지가 있다.

document.domain 변경

부모 창의 도메인은 'abc.tistory.com'이고 자식 창의 도메인은 'def.tistory.com'일 때 두 창의 도메인을 모두 'tistory.com'으로 변경한다. 도메인은 원하는 값으로 마음대로 변경할 수 있는건 아니고 서브도메인을 삭제하는것만 허용된다. 가령 실제 도메인이 'noritersand.tistory.com'일 때 변경 가능한 값은 'noritersand.tistory.com' 혹은 'tistory.com'이다.

document.domain; // 'noritersand.tistory.com'
document.domain = 'aa.tistory.com'; // Error: Illegal document.domain value
document.domain = 'tistory.com'; // 'tistory.com'
document.domain = 'daum.net'; // Error: Illegal document.domain value

부모 창과 같은 도메인으로 이동

먼저 open 함수를 사용하여 다른 도메인의 자식 창을 생성한다.

앞서 말했다시피 도메인이 다르기 때문에 자식 창에선 부모 창에 접근할 수 없다. 따라서 자식 창의 페이지를 부모와 같은 도메인의 페이지로 변경한다. (SUBMIT 혹은 location.href)

이제 도메인이 같아졌으므로 opener를 통해 부모 창 객체에 접근할 수 있다.