CodingBison

For a browser, windows, tabs, frames, or iframes are independent JavaScript execution contexts in JavaScript, where each of these browser elements receive their own global window object. For these cases, individual window, tabs, frames, or iframes do not automatically share each others methods. In this post we focus on accessing variables across two different execution contexts. We use windows and iframes as two examples.

Handling Multiple Windows

Firstly, we look at the case of multiple windows. When dealing with multiple windows, there are two key methods: open() and close(). Let us first begin with signature of these two methods.

 var newWindow = window.open(windowURL, windowName, setOfOptions, needToReplace);
 newWindow.close();

Let us describe the window.open() method. When this method creates a new window, then the new window uses windowURL as the URL to load it page and uses windowName argument as the name of the new window.

The setOfOptions argument provides a list of options that specify the behavior of the new window. As an example, if we pass setOfOptions as "height=200, width=500, toolbar=yes, location=yes, menubar=yes, status=yes", then we specify that the height and width of the window should be 200 pixels and 500 pixels. Also, we specify that the new window should have tool bar, location bar, menu bar, and status bar. If we do not want one or more of these bars, then we can pass a value of "no" to these options. We can also pass a value of 1 or 0 instead of "yes" or "no" values respectively. However, this option is not a standard and it is possible that some of these options do not work in all browsers. In fact, some of the browsers might support additional options as well.

The last option of window.open() is needToReplace. When set, this parameter specifies that the URL of the new window should replace the URL of the parent window in the browser history of the parent window. Else, it creates a new entry in the history list.

The return value of window.open() is a handle of the new window. It is this handle that allows the parent window to access properties (data and methods) of the child window. Once the task of the new window is complete, the parent window can invoke close() call on the handle to close the child window.

Here is an example that opens a new window and then closes the new window after 2 seconds.

 <!doctype html>
 <html>
 <div id="div_id"> </div>
 <script type="text/javascript">

 var elem = document.getElementById("div_id");

 // Close an existing window
 function closeWindow() {
     elem.innerHTML += "<br>Let us close this window"; 
     newWindow.close();
 }

 // Open a new window
 function openWindow() {
     elem.innerHTML += "Let us open a new window"; 
     newWindow = window.open();

     // Set timeout to close the window after 2 second
     setTimeout(closeWindow, 2000);
 }

 // Set timeout to open a window after 1 second
 setTimeout(openWindow, 1000);

 </script>
 </html>

By default, most of the current browsers block pop-ups and hence when running the above program, we may not see the new window, if the pop-ups are disabled. Thus, to see the new window getting opened, we would need to temporarily allow pop-ups. In Firefox, we can go to Edit->Preferences->Content and unselect the "Block pop-up windows" check-box. In Chrome, we can go to Settings->Preferences->"Under the Hood"->"Content Settings..." and select the " Allow all sites to show pop-ups" radio button; the Settings is a small symbol after the URL input text-box in a Chrome window. In Opera browser, we can go to Opera->Settings->Preferences; we can find Opera button before the URL input text-box in an Opera window. Then in the Pop-ups select-box, select the "Open all pop-ups" option. And, for IE ??????

In the previous program, we use close() method of the child window to close the child window. For some use-cases, one might be tempted to close the current window itself! However, Firefox and Chrome do not allow the current window to close itself and so one might find that window.close() or self.close() simply does not work with Firefox and Chrome. Perhaps, the reason for not allowing a script to close the window is that a third-party script can possibly close the window. With JavaScript libraries, it is not uncommon for a browser to run 3rd party JavaScript libraries and a malicious user can close the current window and disrupt the user's experience.

Next, let us look at how one can access methods of the child window and vice-versa. When the parent window creates a new window using "var newWindow = window.open()", then the parent window can always refer to variables and functions of the child window (let us say x) using "newWindow.x".

On the other hand, the child window can refer to the parent window using "window.opener"; thus, if we have to refer to variables and functions of the parent window (let us say y), then we can do that as "window.opener.y".

We provide a simple example where we open a child window from the parent window. Next, we call a parent method (nameParent()) from the child window and call a child method (nameChild()) from the parent window. Here is the code for the main window:

 <!doctype html>
 <html>
 <div id="div_id"> </div>
 <script type="text/javascript">

 / Get a handle for the div element.
 var elem = document.getElementById("div_id");

 // Parent's method 
 function nameParent(callingFrom, whichWindow) {
     return "From the Parent Window";
 }

 elem.innerHTML += "This is the Parent Window: ";
 elem.innerHTML += nameParent("Parent", window);
 elem.innerHTML += ", ";

 // Open a child window.
 var newWindow = window.open("window_multiple_2.html", "child_new_window");

 // Call child's method. But, wait for the new window to load.
 newWindow.onload = function () {
     elem.innerHTML += newWindow.nameChild("Parent", window);
 }

 </script>
 </html>

As noted earlier, we can pass the URL (in this case file name) for the child window to load from. In this example, the parent window passes "window_multiple_2.html" as URL for the new window. It is this file that defines the nameChild() method which is being called by the parent window. Here is the "window_multiple_2.html" file:

 <!doctype html>
 <html>
 <div id="div_id"> </div>
 <script type="text/javascript">

 var elem = document.getElementById("div_id");

 // Child's method 
 function nameChild() {
     return "From the Child Window";
 }

 elem.innerHTML += nameChild("Child", window);
 elem.innerHTML += ", ";

 // Call parents method.
 elem.innerHTML += window.opener.nameParent();

 </script>
 </html>

You might be curious as to why we need to use newWindow.onload event. The onload event accepts a handler (in this case an anonymous function) and calls that function when the window gets loaded. We use the newWindow.onload event because when we call window.open() from the parent window, then it takes some time for the new window to load. Using newWindow.onload ensures that we call the method of the child window only after the child window has loaded.

When we run the first file, we see that a new window is opened. In the parent window, we see an output of "This is the Parent Window: From the Parent Window, From the Child Window". On the child window, we would see an output of "This is the Child Window: From the Child Window, From the Parent Window".

Handling Multiple iframes

After having discussed the case of multiple windows, let us look at iframes (inline frames) and understand how one can access properties of iframes from the main window and vice-versa. iframes are a popular HTML construct when we have to embed a new page (typically, third party page) within the main page. Since JavaScript provides different execution context to iframes, embedding a third party code within an iframe serves us an important line of defense against third-party code.

One can specify an iframe in an HTML page using the <iframe> tag. Let us use an iframe to rewrite the earlier example. With this, we now do not need to call window.open() since the new page gets embedded in the current page itself.

One notable exception is that with the new approach, we do not have an explicit handle for the iframe (earlier window.open() call returned an explicit handle). Instead, we can retrieve a handle from 'document.getElementById("iframe_id").contentWindow'. The document.getElementById() is the familiar method that allows us to get a handle of the document element (with id as "iframe_id" which is the id of the iframe). Next, we use contentWindow() method of the element to get a handle of the iframe. Once we have this handle, we can simply invoke the function of the iframe using this handle.

 <!doctype html>
 <html>
 <div id="div_id"></div>
 <iframe src="iframe.html" 
     width="250" height="100" 
     marginheight="10" 
     marginwidth ="10" 
     id="iframe_id">
 </iframe>

 <script type="text/javascript">

 // Get a handle for the div element.
 var elem = document.getElementById("div_id");
 elem.innerHTML += "<b>This is the Parent Frame:</b> <br>";

 // Get a handle for the iframe.
 var frameHandle = document.getElementById("iframe_id").contentWindow;

 // Parent's function.
 function nameParent(callingFrom, whichWindow) {
     return "From the Parent Window";
 }

 // Call window's function. 
 elem.innerHTML += nameParent("Parent", window);

 // Call iframe's function.
 frameHandle.onload = function () {
     elem.innerHTML += "<br>";
     elem.innerHTML += frameHandle.nameChild("Parent", window);
 };

 </script>
 </html>

Like the case of window.onload event, we now use frameHandle.onload to wait for the new frame to load. Once again, using the onload event ensures that we call the method of the child frame only after the child frame is finished loading.

And here is how the "iframe.html" looks like. With iframe, now this new page would simply get embedded in the parent page. For this iframe, since we do not use window.open(), we cannot use "window.opener.nameParent()" method. Instead, we now need to use the parent object of the iframe window to access parent's method.

 <!doctype html>
 <html>
 <div id="div_id"> </div>
 <script type="text/javascript">

 // Get a handle for the div element.
 var elem = document.getElementById("div_id");
 elem.innerHTML += "<b>This is the Child Frame:</b> <br>";

 // Child's function 
 function nameChild() {
     return "From the Child Window";
 }

 elem.innerHTML += nameChild("Child", window);
 elem.innerHTML += "<br>";

 // Call parent's functions.
 elem.innerHTML += parent.nameParent();

 </script>
 </html>

When we run the window with frame, we find the output shown below. The output shown inside the frame is from the "iframe.html" page. The output shown outside the frame is from the main page that contains the iframe.



Figure: Accessing iframe functions from main window and vice-versa.

Using Frames/iFrames is discouraged!

Before we conclude this topic, let us mention that iframes are a variant of yet another type of HTML construct: frames. It is generally discouraged to use frames/iframes.

Both frames and iframes can potentially prevent search engines from locating contents of frames within a web page. Furthermore, when listing search results, search engines can list frames without a proper context of the main page that includes the frame.

In the past, frames were also used to split the main page into several panels to handle navigation and page-layout. However, with advent of CSS style, we should avoid using frames for page-layout since CSS provides a rich infrastructure for designing a page layout.

An accepted exception to this rule is when we have to host third party content. For such cases, using frames/iframes may be a viable option. Since the content is third party, even if Search Engines ignore that content, it should not hurt the Search Engine Optimization (SEO) aspect of your page. Further, since the third-party content can be potentially malicious, using frames/iframes reduces the security threat.





comments powered by Disqus