아래의 글을 번역한 글입니다.
Popups and window methods
https://ko.javascript.info/popup-windows
팝업창(popup window)은 사용자에게 추가적인 문서를 보여주는 아주 오래된 방법 중 하나다.
기본적으로 아래의 코드를 실행하기만 하면 된다.
window.open('https://javascript.info/')
위의 코드는 위의 주어진 url에 해당하는 새로운 창(window)을 연다.
대부분의 현대 브라우저들은 별도의 새로운 창을 열기보다는,
새로운 탭을 열도록 설정되어 있다.
팝업창(popup)은 아주 고대? 시절부터 존재한다.
초기의 아이디어는 메인창을 닫지 않으면서, 또다른 콘텐츠를 보여주는 것이었다.
현재에는 "또다른 콘텐츠를 보여주기"위한 다른 방법이 있다.
fetch를 이용해서 동적으로 콘텐츠를 로드하고, 로드된 콘텐츠를 동적으로 생성된 div 태그에서 보여줄 수 있다.
그래서, 팝업을 사용하는 일은 요즘은 잘 없다.
또한, 팝업창은 모바일 장치에서는 좀 까다롭다.
모바일 장치는 여러 창을 동시적으로 보여주지 않기 때문에 까다롭다.
여전히 팝업창이 사용되는 작업들이 있다.
예를 들면, OAuth 인증 (구글,페이스북 등을 이용한 소셜 로그인).
왜 팝업이 사용되는가하면..
과거에 악성 사이트를 팝업창을 많이 남용했다.
나쁜 페이지는 수십개의 광고 팝업창을 열 수 있었다.
그래서, 지금은 대부분의 브라우저들은 팝업창을 차단해서 사용자를 보호하려고 노력한다.
대부분의 브라우저들은
만약 팝업창들이 "onClick"과 같은 유저의 실행으로 인해 실행되는 이벤트를 제외한
다른 원인으로 실행되는 팝업을 차단한다.
// popup blocked
window.open('https://javascript.info');
// popup allowed
button.onclick = () => {
window.open('https://javascript.info');
}
이렇게 하면,
원치않는 팝업들로 부터 사용자들이 보호될 수 있다.
하지만, 팝업기능 자체가 완전히 disable되는 것은 아니다.
만약 팝업이 "onClick"에 의해서 열리는데,
"setTimeout"이후라면 어떨까?
좀 어려운 문제다.
// open after 3 seconds
setTimeout(() => window.open('http://google.com'), 3000);
위의 코드는
구글 크롬 브라우저에서는 팝업이 열리지만,
파이어 폭스 브라우저에서는 차단된다.
setTimeout(() => window.open('http://google.com'), 1000);
delay 시간을 줄인 위의 코드는
구글 크롬 브라우저, 파이어 폭스 브라우저 둘 다에서 팝업이 실행된다.
이렇게 차이가 나는 이유는,
파이어폭스는 2000ms 또는 그 이하의 timeout을 허용하기 때문이다.
하지만, "trust"를 삭제하고, "사용자의 행동의 밖"이라고 가정하면.
첫번째는 차단된고, 두번째는 아니다.?!
팝업을 여는 문법은 "window.open(url, name, params)
새로운 창(window)에 로드할 url
새로운 창(window)의 이름.
각 창에는 "window.name"이 있다.
그리고, 팝업으로 어느 창을 사용할 지 명시할 수 있다.
같은 이름의 창이 이미 존재하면, 그 창에 주어진 url이 열리고,
같은 이름의 창이 존재하지 않으면, 새로운 창을 열러 주어진 url을 연다.
새로운 창을 위한 "설정 문자열"
이 문자열에는 콤마(,)로 구분된 설정정보가 들어가 있다.
파라미터 사이에는 공백이 있으면 안된다.
파라미터의 예시:
width=200,height=100
params을 통해 설정할 수 있는 것들:
특정 브라우저들만이 제공하는 기능(feature)들이 있음(대부분은 잘 안쓰임)
자세한 사항은 아래의 링크 참조
window.open in MDN
브라우저가 어떠한 기능들을 disable했는지 살펴보기 위해, 최소한의 기능만 설정된 창을 열어보자.
let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=0,height=0,left=-1000,top=-1000`;
open('/', 'test', params);
위의 코드에서 대부분은 윈도우 기능을 disable했고,
윈도우 포지션은 offscreen이다(left=-1000,top=-1000)
위의 코드를 실행시켜서 무엇이 벌어지는 지 살펴보자.
위의 코드를 실행시킨 결과 화면 캡쳐(크롬):
대부분의 브라우저는 "width/height이나 offscreen left/top의 값이 0인 경우"와 같은 이상한 것들을 수정해준다.
예를 들어, 크롬은 이상한 경우("width/height이나 offscreen left/top의 값이 0인 경우") 윈도우 창을 full width/height로 열어서, 새로운 창이 전체 스크린을 차지하도록 한다.
이번에는, 정상적인 poisition 옵션들을 넣고, 적당한 width/height/left/top 값을 넣어서 실행해보자.
let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=600,height=300,left=100,top=100`;
open('/', 'test', params);
위의 코드를 실행시킨 결과 화면 캡쳐(크롬):
대부분의 브라우저들은
필요한 만큰 위와 같이 보여준다.
제외된 설정들에 대한 규칙들:
open call은 새로운 창에 referecne(새로운 창을 참조할 수 있는 값)를 리턴한다.
리턴된 reference를 이용해서 새로운 창의 값(property)을 조작하거나, 위치를 변경하거나 하는 등 이상의 작업을 할 수 있다.
JavaScript로부터 팝업 콘텐츠를 생성해보자.
let newWin = window.open("about:blank", "hello", "width=200,height=200");
newWin.document.write("Hello, world!");
로딩 이후, 콘텐츠를 수정하였다.
let newWindow = open('/', 'example', 'width=300,height=300')
newWindow.focus();
alert(newWindow.location.href); // (*) about:blank, loading hasn't started yet
newWindow.onload = function() {
let html = `<div style="font-size:30px">Welcome!</div>`;
newWindow.document.body.insertAdjacentHTML('afterbegin', html);
};
파이어폭스 실행화면:
주의: window.open 실행 바로 이후에, 새로운 창이 아직 로드되지 않았다. (alert 실행을 통해 알 수 있음)
그래서, 그것을 수정하기 위해 onload를 기다린다.
또한, newWin.document를 위한 DOMContentLoaded 핸들러를 사용할 수 있다.
Same origin policy (같은 오리진 정책)
창(window)은 오직 같은 오리진(같은 프로토콜://도메인:port)에서 왔을 때는 자유롭게 콘텐츠를 접속할 수 있다.
그렇지 않으면,
사용자 안전상의 이유로,
메인 윈도우가 "site.com"이라는 오리진에서 왔고,
팝업창은 "gmail.com"에서 오는 경우는 불가능하다.
보다 자세항 사항은 아래의 챕터를 살펴보자(Cross-window communication): https://ko.javascript.info/cross-window-communication
팝업 창은 window.opener 레퍼런스를 사용해서 "opener" 윈도우를 접근할 수 있다.
팝업 창을 제외한 모든 윈도우에서는 "window.opener 레퍼런스"는 null이다.
아래의 코드를 실행하면,
opener(current) 윈도우 콘텐처를 "Test"로 바꾼다.
let newWin = window.open("about:blank", "hello", "width=200,height=200");
newWin.document.write(
"<script>window.opener.document.body.innerHTML = 'Test'<\/script>"
);
위의 코드 실행화면(파이어폭스)
즉, 윈도우 간의 커넥션은 양방향이다: 메인 윈도우와 메인 윈도우로부터 생성된 팝업 윈도우는 서로 간의 reference(참조)를 갖는다!
윈도우 창 닫기: win.close()
윈도우 창이 닫아졌는지 확인하기 위해서는: win.closed
기술적으로, close() 메소드는 어떠한 윈도우에도 사용할 수 있다.
하지만, window.close()는 대부분의 브라우저에서 무시된다.
window.close()는 오직 window.open()에 의해 생성된 윈도우(즉, 팝업창)에만 작동한다.
만약, 윈도우가 닫히면, closed 속성은 true가 된다.
이는 팝업창 또는 메인 윈도우가 여전히 열려있는지 아닌지를 확인할 때 유용하다.
사용자는 창을 언제든 닫을 수 있으며, 코드는 창을 닫는 것을 고려해야만 한다.
아래의 코드는 창을 로드하고 닫는다.
let newWindow = open('/', 'example', 'width=300,height=300');
newWindow.onload = function() {
newWindow.close();
alert(newWindow.closed); // true
};
위의 코드를 파이어폭스에서 실행한 화면 캡쳐:
창을 move/resize하는 메소드들
window.onresize 이벤트라는 것도 있음
오직 팝업창만!
오남용을 예방하기 위해서, 브라우저는 이러한 메소드들을 차단한다.
부가적인 탭이 없는, 우리가 연 팝업창에서만 작동한다.
최소화/최대화 없음!
자바스크립트는 창을 최소화/최대화할 수 있는 방법이 없다.
창 최소화/최대화는 OS레벨의 기능이다. (프론트엔드 개발자가 할 수 없는 부분)
move/resize 메소드는 최대화/최소화를 위해 동작하지 않는다.
"브라우저 창 사이즈와 스크롤" 챕터에서 윈도우 스크롤링에 대해 언급한바 있다.
window.onscroll 이벤트도 있다.
이론적으로, 윈도우를 focus/unfocus하기 위해서 window.focus()와 window.blur()가 메소드가 있다.
또한, 방문객이 윈도우를 focus하거나 다른 곳으로 focus를 하는 순간을 캐치하기 위한 focus/blur 이벤트가 있다.
비록, 실용적인 측면에서 제약들이 있다.
왜냐하면, 과거에 악성 페이지들이 이를 남용했었기 때문이다.
아래의 코드를 살펴보자:
window.onblur = () => window.focus();
사용자가 윈도우 밖으로 전환(window.onblur)하려고 할 때, 다시 윈도우로 focus를 가져온다.
의도는 사용자는 윈도우 안으로 가두기(lock) 위함이다.
그래서, 브라우저들은 이와 같은 코드를 금지하기 위해서(이와 같은 코드를 사용하는 악의적인 페이지와 광고들로 부터 사용자를 보호하기 위해서) 많은 제약사항들을 제안하고 있다.
이러한 코드의 구현은 브라우저에 의존하고 있다.
예를 들면,
모바일 브라우저는 보통 window.focus()를 완젼히 무시한다.
또한, focusing은 팝업창이 별개의 탭의 형태로 실행될 때 작동하지 않는다. (새창으로 팝업이 생기는 경우는 동작)
여전히, focus가 효과적인 사례들이 있음:
팝업창은 드물게 사용된다.
팝업창 대신 페이지 내에서 로딩/display하거나, iframe을 사용하는 방법들 등의 방법들이 존재하기 때문에.
만약 우리가 새로운 팝업 창을 열려고 한다면, 좋은 관례는 사용자에게 이를 알리는 것이다.
링크/버튼 주변에 "opening window" 아이콘을 표시함으로써 방문객들이 focus 이동이나 두개의 창을 모두 염려해 둘 수 있도록 할 수 있다.
팝업창을 닫기 위해서는 close()를 사용한다.
또한, 사용자가 창을 닫을 수 있다.
창이 close()에 의해 닫히던, 사용자가 창을 닫아서 닫히던, 닫히면
window.close()값은 true이다.