Popups and window methods

A popup window is one of the oldest ways to put a second document in front of a visitor. The whole feature predates most of the modern web, yet a single line still opens one:

window.open('https://example.com/')

That call asks the browser for a new window loading the given URL. What you actually get depends on the browser and how you called it: most browsers open a new tab, not a floating window, unless you pass sizing options.

The original point of popups was to show extra content without navigating away from the current page. Today you have gentler tools for that — load data with fetch and render it into a <div> you build on the fly — so popups are no longer an everyday reach. They also behave poorly on phones, which usually can’t display two windows side by side.

opener window
your page

open() →← opener

popup window
separate JS env

A popup is a full, separate browsing context — its own window object, its own JavaScript environment — linked back to the page that opened it.

So why keep them around at all? OAuth sign-in flows (“Log in with Google”, “Continue with GitHub”) lean on popups because the shape fits the problem:

  1. A popup is a separate window with its own independent JavaScript environment. Opening one from an untrusted third-party page is safe — the popup’s page can’t reach into the opener’s script unless they share an origin.
  2. Opening one takes a single call.
  3. The popup can navigate to new URLs and pass messages back to the window that opened it.

That combination — isolated, cheap to open, still able to talk home — is exactly what a login handshake needs.

Popups earned a bad reputation. In the early days, hostile pages would spawn stacks of ad windows the moment you arrived. Browsers responded by blocking them, and the rule they settled on is simple to state:

// blocked: runs on page load, no user action behind it
window.open('https://example.com');

// allowed: runs because the user clicked
button.onclick = () => {
  window.open('https://example.com');
};

This keeps popups usable for genuine interactions while shutting down the drive-by ad flood.

page load / timer
no user gesture
open() → blocked
onclick / keydown
user gesture present
open() → allowed
The same open() call is treated differently depending on what triggered it.

window.open

The full signature is window.open(url, name, params):

url

The URL to load into the new window.

name

A name for the new window. Every window carries a window.name, and this argument says which window to target. If a window with that name already exists, the URL loads into it; otherwise the browser opens a fresh one. Reusing a name is how a “help” link can keep reopening the same secondary window instead of piling up new ones.

params

A configuration string for the new window: settings separated by commas, with no spaces. For example width=200,height=100.

Here’s how those three pieces fit together:

‘/page’
url
what loads
‘help’
name
which window
‘width=600,height=300’
params
size & chrome
The three arguments to window.open and what each controls.

Settings you can put in params:

  • Position and size:
    • left/top (numeric) — screen coordinates of the window’s top-left corner. A new window can’t be pushed off-screen.
    • width/height (numeric) — the window’s size. There’s a minimum, so you can’t create an invisible window.
  • Window chrome (yes/no toggles):
    • menubar — show or hide the browser menu.
    • toolbar — show or hide the navigation bar (back, forward, reload).
    • location — show or hide the URL field. Some browsers won’t let you hide it.
    • status — show or hide the status bar. Most browsers force it on.
    • resizable — allow disabling the resize handle. Not recommended.
    • scrollbars — allow disabling scrollbars. Not recommended.

There are a few more browser-specific flags that almost nobody uses. The window.open reference on MDN has the full list.

Example: a minimalistic window

Try opening a window with as much stripped away as possible, to see what the browser actually lets you turn off:

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);

Most window features here are set to no, and the window is aimed off-screen at a zero size. Run it, and you’ll see the browser “fix” the impossible parts. Chrome, for instance, refuses the zero width/height and the off-screen left/top, and opens something visible and reasonably sized instead. The browser protects the user from a window that would be hidden or unusable.

Now give it sane numbers:

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);

With real coordinates and a real size, most browsers honor the request.

Because the params argument is just a comma-separated string with no spaces, it’s easy to get wrong by hand. Set the size and toggle a few features below and watch the exact string window.open would receive take shape:

interactiveBuild a window.open params string

Accessing the popup from the opener

open returns a reference to the new window, and that reference is a live handle onto it — you can read and change its properties, its location, and (same origin permitting) its document.

Here we build the popup’s content entirely from JavaScript:

let newWin = window.open("about:blank", "hello", "width=200,height=200");

newWin.document.write("Popup is live.");

about:blank gives you an empty same-origin document to write into. Writing to another site’s document wouldn’t be allowed.

A real popup can’t run in this lesson’s sandbox, but the same idea works with any separate same-origin browsing context — here a mini window stands in for the popup. Edit the HTML and press the button to see the opener drive the other document’s content from the outside:

interactiveWriting content into a separate window from the opener

Modifying content after the page loads is a little more involved, because the load doesn’t happen instantly:

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">You're in!</div>`;
  newWindow.document.body.insertAdjacentHTML('afterbegin', html);
};

The alert on line (*) is the point to notice. Right after window.open returns, the new window still holds the blank starting document — the requested URL hasn’t finished loading, so its location.href reads about:blank. Touch document.body at this moment and it may not be there yet. Wait for onload before you reach into the document. (A DOMContentLoaded handler on newWindow.document works too, and fires slightly earlier.)

open() returns
handle ready
document
= about:blank
→ loading →
onload fires
safe to edit
Right after open() returns the document is still blank. The real page arrives later, and onload marks the moment it's safe to modify.

Accessing the opener from the popup

The link runs both ways. A popup reaches its opener through window.opener. For any window that wasn’t opened as a popup, window.opener is null.

This popup writes a script that reaches back and overwrites the opener’s body:

let newWin = window.open("about:blank", "hello", "width=200,height=200");

newWin.document.write(
  "<script>window.opener.document.body.innerHTML = 'Popup was here'<\/script>"
);

(The <\/script> escape keeps the HTML parser from ending the outer script tag early.) After this runs, the opener page shows just “Popup was here” — the popup drove a change in its parent.

So the two windows hold references to each other: the opener has the value returned by open, and the popup has window.opener.

opener
newWin = open(…)

newWin →← window.opener

popup
window.opener

The bidirectional link: the opener keeps the return value of open(), the popup keeps window.opener.

Closing a popup

Close a window with win.close(). Check whether it’s closed with win.closed.

Technically every window has a close() method, but most browsers ignore window.close() unless the window was created by window.open(). In practice, you can reliably close only popups you opened, not the main tab a user navigated to directly.

The closed property becomes true once the window is gone. It matters because a user can close a popup at any time, and your code should assume they might. Reaching into a closed window’s document throws or does nothing useful, so guard with if (!win.closed) before you touch it.

let newWindow = open('/', 'example', 'width=300,height=300');

newWindow.onload = function() {
  newWindow.close();
  alert(newWindow.closed); // true
};

Because the user can close a popup at any moment, code that reaches into one should check closed first. This demo mimics a window handle whose closed flag flips when it’s shut — try to “focus” it before and after closing, and notice how the guard keeps the code from touching a window that’s gone:

interactiveGuarding with win.closed

Moving and resizing

A window handle carries methods to reposition and resize it:

win.moveBy(x, y)

Move the window x pixels right and y pixels down from its current spot. Negative values move it left and up.

win.moveTo(x, y)

Move the window so its top-left corner sits at screen coordinates (x, y).

win.resizeBy(width, height)

Grow or shrink the window by the given amounts relative to its current size. Negative values shrink it.

win.resizeTo(width, height)

Set the window to an exact size.

There’s also a window.onresize event to react when the size changes.

positionsize
relativemoveBy(x, y)resizeBy(w, h)
absolutemoveTo(x, y)resizeTo(w, h)
moveBy/resizeBy are relative to the current geometry; moveTo/resizeTo set absolute values.

Scrolling a window

Scrolling was covered back in Window sizes and scrolling; the same methods apply to a popup handle.

win.scrollBy(x, y)

Scroll x pixels right and y pixels down from the current scroll position. Negatives scroll the other way.

win.scrollTo(x, y)

Scroll to the absolute coordinates (x, y).

elem.scrollIntoView(top = true)

Scroll so that elem lines up at the top of the viewport (the default), or at the bottom with elem.scrollIntoView(false).

And there’s a window.onscroll event for reacting to scrolling.

Focus and blur on a window

In theory, window.focus() and window.blur() bring a window to the front or send it back, and the focus and blur events fire as the visitor moves attention onto a window and away from it.

In practice these are heavily restricted, again because bad pages abused them. The classic offender:

window.onblur = () => window.focus();

Every time the user tries to leave the window (onblur), it yanks itself back into focus — a trap that pins the visitor in place. To shut that pattern down, browsers piled on limits, and the exact behavior varies by browser. A mobile browser typically ignores window.focus() outright, and focusing also fails when a “popup” actually opened as a separate tab.

There are still legitimate uses that work:

  • After opening a popup, calling newWindow.focus() is a reasonable nudge — on some OS and browser combinations it helps ensure the user’s attention lands on the new window.
  • To track whether a visitor is actively using your app, listen for window.onfocus and window.onblur. You can pause animations or polling on blur and resume on focus. Keep in mind that blur only means the window lost focus — it may still be visible in the background, so don’t assume the user can’t see it.

Summary

Popups are a niche tool now — in-page rendering or an iframe usually beats them. When you do open one, tell the user it’s coming: a small “opens a new window” marker next to the link or button softens the focus shift and keeps both windows in the visitor’s mental map.

  • Open a popup with open(url, name, params). It returns a reference to the new window.
  • Browsers block open calls that don’t come from a user action; a notification usually lets the user allow them anyway.
  • Without sizing params you typically get a new tab; provide width/height and you get a popup window.
  • The popup reaches its opener through window.opener.
  • Same-origin windows can read and modify each other freely. Cross-origin windows are limited to changing each other’s location and exchanging messages.
  • Close a popup with close(); the user can also close it. Afterward window.closed is true.
  • focus() and blur() focus and unfocus a window, but they don’t always work.
  • The focus and blur events track attention moving in and out — remember a blurred window may still be visible in the background.