Detect Popup Window Closing in Lightswitch

My last adventure with Lightswitch was trying to detect when a popup window inside a screen is closed.

You can close a Lightswitch popup window by clicking or tapping outside the popup. The jQuery Mobile popup widget that Lightswitch uses then closes the popup. However, I wanted to intercept that event and do some housekeeping such as discarding the changes made by the popup.

jquery mobile popup

The difficult thing was finding out where to put the event hookup code. Then, it was just a question of using jQuery Mobile Widget afterclose event that is triggered when a popup is closed.

The right event to listen for in my case was the rendering of the popup content. In the Lightswitch designer add a postRender event handler and associate the afterclose event of the parent object (the popup itself):

Lightswitch Deep Linking and Navigation in SharePoint Dialog Frame

Another one of my strange adventures with Lightswitch has made me look deep inside the navigation framework that Lightswitch uses. I will explain the strange symptoms, the cause I found out and the solution that worked for me.

SYMPTOMS

I have a Lightswitch app connected to SharePoint data. I have several screens, one for each list that is managed from Lightswitch. I want to be able to navigate straight to the screen I want from SharePoint, or to deep-link the screen.

To do so, I made a custom Promoted Links list. I added to this list my LS app deep URLs, such as https://appurl/HTMLClient/default.htm?SPHostUrlaaa&SPAppUrl=bbb&SPChromeColor=ccc#/ScreenName/[unique_number]. The ScreenName parameter is different for each screen, obviously. I also configured each link to open in a SharePoint dialog window.

Strange things happened. While I could open the desired screen by clicking on the promoted link, it would start displaying “Error Loading Page” message and keep a /spinning circle icon alive as if it were waiting for a long-running operation, after every navigation to and back from another screen or popup in the app.

image

These symptoms would disappear if the links were opened without dialogs (in the same tab or another browser tab). They would also disappear if I switched to another screen inside the app using the Lightswitch navigation menu.

CAUSE

I first suspected any custom JS files I added to the application. However, it was not until I used Fiddler tool to investigate the requests my app was doing that I found the answer.

Lightswitch app are Single-Page Applications (SPA). They leverage jQuery Mobile framework for Lightswitch navigation and maintaining app state in the browser. jQuery Mobile has a custom navigation feature that overrides standard browser navigation stack to be able to use Back button to go to the previous screen. In a SPA, there is only one “physical” page (default.htm) and all the further requests are done using AJAX calls to the LS backend services.

Fiddler showed me that the error messages were caused by a 404 HTTP not found response to a unknown page that was something like this: /ScreenName/IsDlg=1.

By opening any URL inside a SharePoint dialog, SharePoint automatically adds IsDlg=1 to the URL. It is used by SharePoint to hide any extra chrome such as navigation and toolbar from being displayed inside a dialog. So, my original URL was now changed to as https://appurl/HTMLClient/default.htm?SPHostUrlaaa&SPAppUrl=bbb&SPChromeColor=ccc#/ScreenName/[unique_number]&IsDlg=1. I could now reproduce the error even without SharePoint dialog, just by adding an extra query string parameter to the LS deep link URL and pasting it in the browser address bar.

Well, jQuery Mobile powered Lightswitch navigation seems to break when trying to parse a screen ID that follows the # sign and then finds another query string parameter such as &IsDlg=1. I tried to move the IsDlg parameter to the left of the screen ID. It worked, and I saw that I had to fix the URL in order for the navigation to work.

SOLUTION

I first thought of rewriting the LS URL by adding a custom HTTP module or a IIS rewrite rules but it sounded too cumbersome. I had to find a simpler mechanism to achieve the opening of a specific LS screen in a SharePoint dialog frame.

Thanks to my colleague Sergio Gallego, I was able to find a solution. I made a copy of the default.html page in LS and called it screen.htm. I then changed the ready() event handler inside the page to check for a query string parameter called “s” (for screen). It would then pass the parameter value as a parameter to the LS msls._run() method. This method takes an optional string parameter that is then used to start the LS app and open the screen with that same name.

I had to rewrite my URLs to the following schema: as https://appurl/HTMLClient/screen.htm?SPHostUrlaaa&SPAppUrl=bbb&SPChromeColor=ccc&s=ScreenName.

Here is the full code of the ready() method in the screen.htm file.

How to Enable Custom JavaScript on MDS Pages in SharePoint 2013

If you have custom JavaScript file loaded in your master page, as we usually do in SharePoint, you might have stumbled upon the problems with custom JS and SharePoint 2013 new Minimum Download Strategy (MDS).

By default, MDS is enabled on Team Sites in SharePoint 2013 and allows for refreshing data on SharePoint pages without causing reloads. But, in order to do so, all the content and JavaScript in MDS pages must play along nicely. If not, the symptoms include:

  • blank page on loading custom JS in a MDS-enabled page
  • custom JS script not loading

The solution

The first part is to use ScriptLink control in the master page, instead of using script tags directly. Specify "LoadAfterUI" attribute in order for the script to be loaded after the page is loaded in MDS.

<SharePoint:ScriptLink language="javascript" ID="Whatever" name="~sitecollection/Style Library/js/yourcustom.js" OnDemand="false" LoadAfterUI="true" runat="server" Localizable="false" />

The second part is to encapsulate all your custom JS in a single function and call it from your custom code. Your yourcustom.js file should look like this:

function $_global_customjs(){
    _spBodyOnLoadFunctionNames.push(‘DoSomething’);
}
    var DoSomething = function ()
    {
        // — Your custom JS here
    }
$_global_customjs();

[Reciclaje JavaScript] Encapsulamiento

Continuando con la serie de posts sobre como programar bien en JavaScript, en este post vamos a ver el tema de encapsulamiento. Como sabéis, la programación orientada a objetos introduce el encapsulamiento como herramienta para ocultar las partes privadas de un objeto de los consumidores de su parte pública. Es decir, el encapsulamiento nos sirve para proteger la implementación interna de las funcionalidades del objeto.

encapsulation

Los ámbitos de variables

En JavaScript conviene saber una cosa antes de enseñar el encapsulamiento y es nada menos que saber como funcionan los ámbitos de variables.

En C# tenemos los siguientes ámbitos de variables

  • de bloque (dentro de un if, o dentro de un for)
  • de método (dentro de un método)
  • de clase (variables disponibles a todo el código dentro de una clase)

En JavaScript tenemos sólo un ámbito de variable

  • de función

En JavaScript, la variable "sube" hasta la función más cercana y allí se queda. No estará disponible para el código JavaScript fuera de esa función pero sí al código que haya dentro de la función. Para que la cosa sea aún peor, la variable hace el "hoisting" y puede machacar las variables con el mismo nombre, como se recoge en el siguiente ejemplo:

var miVariable = 42;
function foo() {
    alert("Espero 42: " + miVariable);
    miVariable = 123; 
    if (true) {
        var miVariable = 21;
    }
}
foo();

Aquí podemos ver que la variable miVariable dentro del bloque if "sube" hasta la función foo() y se queda como "interna". Sin embargo, en el primer alert el valor de la variable será "undefined". La variable externa miVariable se quedará con el valor 42.

Es importante entender el comportamiento de este trozo de código. Si necesitáis refrescar conceptos, repasad el artículo anterior de la serie de posts.

Variables "globales" y el objeto Window

¿Qué pasa con la variable externa miVariable? Esta variable no tiene ninguna función a la que subir, ¿verdad? Pues resulta que en JavaScript toda variable no asignada dentro de una función sube hasta el objeto window que está disponible en todo el runtime de JavaScript.

Podemos decir que declarar una variable "global" y una variable dentro de window es equivalente. Podéis comprobar este comportamiento en este enlace.

Cierres (Closures)

En muchos lenguajes de programación existe el concepto de cierre (closure). Un cierre es básicamente el mantenimiento del valor de una variable después de que el proceso en el que se ha usado esa variable haya acabado. Es como si el valor de esa variable esté "cerrado" dentro del proceso.

http://edinkapic.azurewebsites.net/wp-content/uploads/2013/07/closure-229x300.png

Para explicar este concepto, nada mejor que un ejemplo.

var FactoriaSaludos = function (nombre) {
    var miPrefijo = ‘Hola, ‘;
    return function() {
        return miPrefijo + nombre;
    };
}

var saludador = FactoriaSaludos(‘Edin’);
alert(saludador());

En este ejemplo tenemos una función FactoriaSaludos que toma un parámetro nombre. Dentro de ella, devolvemos otro objeto función. Lo que pasa es que esa función anónima interna usa variables que están fuera de su ámbito (miPrefijo y nombre). En este caso, el objeto devuelto (saludador) de tipo función tendrá "cerradas" dentro de él esas dos variables.

Por eso, al invocar la función saludador, nos devuelve un saludo con los valores "cerrados" en el closure de la función interna.

Si este primer ejemplo no te ha parecido muy complejo, prueba con el segundo.

for (var i = 0; i < 10; i++) {
  document.getElementById(‘box’ + i).onclick = function() {
    alert(‘You clicked on box #’ + i);
  };
}

En este ejemplo, al clicar en un div con el nombre box1 esperamos obtener el mensaje de "You clicked on box #1". Sin embargo, podéis verificar que todos los divs devuelven el mismo valor: 5.

¿Qué ha pasado? Pues que la función anónima de onclick ha capturado la variable i. Al clicar, se invoca el valor de la i (es decir, el cierre tiene la variable capturada por referencia) que será siempre el último en actualizarse (es decir 5).  Para prevenir esto tenemos que introducir una función interna más, que asegurará que el valor capturado será el del parámetro de entrada de la función externa, evitando la referencia directa a la variable i.

for (var i = 0; i < 10; i++) {
  document.getElementById(‘box’ + i).onclick = function(index) {
    return function() {
      window.alert(‘You clicked on box #’ + index);
   }
  }(i);
}

La función asignada al onclick toma como parámetro index el valor de la variable i y la función interna lo recoge como cierre . Si ahora clicamos en cada caja, muestra su valor correcto.

¿Todavía no tienes claro como funcionan los cierres? No te preocupes, los cierres parecen muy raros hasta que se prueban en el código. Juega con JSFiddle y prueba los ejemplos que hay en internet hasta que lo entiendas bien.

En la próxima entrega veremos como podemos jugar con cierres para simular encapsulamiento público y privado en JavaScript.

[Reciclaje JavaScript] Fundamentos

Tal como dije en mi post del año pasado, una de las cosas que íbamos a aprender los desarrolladores de SharePoint con la nueva versión es precisamente a programar bien en JavaScript. Yo mismo me puse al día por necesidad y he descubierto que no es tan difícil programar bien JavaScript, si se entienden algunos conceptos clave. Mi idea en esta serie de posts es explicar estos conceptos de manera sencilla para que se entiendan bien. Intentaré no ser muy purista para no alejar la audiencia, que considero que tiene cierto conocimiento de JavaScript pero como una cosa totalmente auxiliar al código NET.

Puntos básicos de JavaScript

A pesar de su merecida o inmerecida fama de lenguaje “de juguete”, JavaScript es un lenguaje muy versátil y potente. Lo primero que hay que desaprender es que JavaScript no tiene nada que ver con Java (su nombre respondía a una estrategia de marketing de Netscape). JavaScript es un lenguaje funcional “vestido” de un lenguaje imperativo “tipo C”.

¿Funcional? Sí, en JavaScript las funciones son los reyes del mambo. Una función en JavaScript es objeto de primer nivel y se puede usar como resultado o parámetro de otras funciones. Una función puede tener parámetros pero éstos no son obligatorios cuando se invoca la función. Lo veremos todo más claro un poco más adelante, tened paciencia.

Tipos de datos

Todo lenguaje tiene una serie de tipos de datos básicos. JavaScript también. En esta tabla recogemos los tipos de datos de JavaScript:

Sencillos

Complejos

  • Number
  • String
  • Boolean
  • undefined
  • null
  • Object
  • Array
  • Function
  • RegExp
  • Date

A continuación voy a explicar las frikadas peculiaridades de cada tipo de datos, excepto RegExp y Data que son bastante especializados.

Wrappers

La mayoría de tipos en JavaScript se exponen dos veces: una como tipo primitivo y otra como “clase”. Por ejemplo, una cadena de texto en JavaScript es un tipo primitivo llamado string pero expone métodos como p.ej. toUpperCase() como si fuera un objeto. Realmente, hay un “objeto” de tipo String que encapsula (“wrapea”) un tipo primitivo string. Dicho esto, en JavaScript casi siempre es mejor usar el tipo primitivo que el tipo encapsulado, por tanto es mejor usar el constructor de objeto ({}) y de matriz ([]) y no los constructores new Object() y new Array().

Number

Todos los números en JavaScript son números decimales de 64 bits en coma flotante. Teniendo en cuenta esto, hay que usarlos muy cuidadosamente para cálculos intensivos, ya que las operaciones decimales siempre son mucho más lentas que los números enteros. Algunas implementaciones de JS optimizan el tipo de datos numérico a enteros cuando no hay decimales involucradas, pero siempre hay que partir de la base de que en JS los números son “caros” para realizar operaciones.

String

El texto se representa en JavaScript como Unicode. Podemos usar las comillas simples o dobles para incluir el valor literal de un string, pero hay que usar dos iguales. Si queremos “escapar” las comillas para incluirlas en el texto, basta con usar las comillas opuestas, p.ej. var textoConComillas = "texto ‘entre’ comillas";

Además, podemos codificar un string para incluirlo en la URL (que no permite ciertos caracteres) y para ello tenemos 2 instrucciones:

  • encodeURI / decodeURI de usan para direcciones URL/URI completas
  • encodeURIComponent / decodeURIComponent se usa para los valores de los parámetros en una URL/URI (es decir, los caracteres como & se escaparán)

Boolean

El tipo booleano tiene valores true o false (sin comillas). La diferencia es que en JavaScript todos los tipos de datos evaluan a un booleano. Es lo que se conoce como valores “truthy” o “falsey”.

Valores que evaluan a FALSE

Valores que evaluan a TRUE

  • false
  • 0 (cero)
  • "" o (cadena vacía)
  • null
  • undefined
  • NaN (resultado de división por cero)
  • todos los demás (incluso las cadenas de texto “false” o “0”)
  • objetos vacíos (p.ej. var a = {};)

De esta manera podemos simplificar las comparaciones: if(a == “” || a == undefined) pasa a ser if(a)

Undefined

Con esta piedra tropieza todo programador de NET en JavaScript. La regla más fácil para recordar es que undefined es una referencia nula en JavaScript. Si no declaramos una variable o la declaramos pero no le asignamos nada, en JavaScript esa variable será del tipo undefined.

Lo que es un null en NET, es undefined en JavaScript.

Null

A pesar de su parecido con NET, el null de JavaScript es un mero marcador de valor nulo que podemos poner para indicar que una variable no tiene un valor significativo, parecido al NULL de SQL.

Es importante saber que JavaScript nunca devolverá un null a no ser que lo pongamos nosotros a una variable.

Object

Tal como he mencionado anteriormente, los objetos en JS no son tan “fuertes” como en NET. No existe el concepto de clase ni de instancia.

Para instanciar un objeto podemos usar:

El constructor usando la notación “orientada a objetos” var a = new Object();
a.nombre = ‘Edin’;
El constructor literal usando la sintaxis JSON var a = { nombre: ‘Edin’ };
Una función, usando la palabra clave this para referirnos a la “instancia” actual de la función. var a = function()
{
this.nombre = ‘Edin’;
return this;
}();

En el último ejemplo hay dos paréntesis después de la declaración de la función. Son muy importantes. Sirven para ejecutar la función y devolver el resultado. Si no los ponemos, nuestra variable a sólo contendrá la definición de la función (es decir, será de tipo Function) y no el objeto que la función devuelve.

image image

¿Y si nos olvidamos de poner return this; dentro de la función? La función devolverá un objeto vacío (porque no hay ninguna clausula return) que no tiene definido el método (mejor dicho, la función) llamado "nombre". Por tanto, el typeof sería undefined. Es muy importante entender y asimilar este punto para evitar sorpresas con JavaScript.

Array

Las matrices (arrays) se definen en JS con un objeto Array o bien con el literal [] separando los elementos con comas. Empiezan siempre en la posición 0 y pueden contener elementos de tipos diferentes (no son tipadas). Incluso podemos tener funciones u otros arrays como elementos de un array.

El número de elementos de un array se obtiene con la propiedad length.

var array = [‘Edin’, 8.123, "Kapic"];
var longitud_array = array.length;

Function

Como hemos visto antes, las funciones son mucho más "maleables" en JavaScript que en NET. Hay dos maneras de declarar una función.

La primera y la más frecuente es usar la sintaxis NET y escribir una declaración:

function miFuncion(parametros)
{
  /* el código de la función */
}

La otra manera es asignar la función a una variable como una expresión:

var miFuncion = function (parametros)
{
  /* el código de la función */
};

Notad que cuando usamos una expresión, hay que poner punto y coma al final, ya que no deja de ser una asignación de un tipo de datos Function a una variable que se llama miFuncion.

Las dos maneras son equivalentes pero la primera (la declaración) tiene el efecto no deseado de hoisting. Se trata de que JavaScript automáticamente sube la declaración de la función arriba de todo el código JavaScript (mejor dicho hasta su contenedor más alto). Siempre que veamos una declaración, hay que imaginarse una variable con el nombre de la función, puesta arriba del todo e inicializada.

/* codigo */ function a() { … }  es lo mismo que var a = function() { … }; /* codigo */

En el ejemplo siguiente se observa el efecto pernicioso de hoisting:

function f() {
    /* es como si aquí pusiera: var g = function()… */
    var g = 1; 
    function g() {
        alert("hola");
   }
    g();
}

La función g() es una declaración, sube por encima de var g y por tanto se pierde al inicializar g con el valor numérico. La ejecución g() no será válida.

Si lo cambiamos a una expresión, entonces la última asignación de g será la de la función y todo funcionará tal como esperamos.

function f() {
    var g = 1; 
    var g = function() {
        alert("hola");
    }
    g();
}

Variable Hoisting

El maldito hoisting existe también para las variables. Si ponemos var nombre = valor en un código, JavaScript lo va a separar en dos al ejecutar: var nombre arriba del todo y nombre = valor en el sitio donde está la expresión.

var a = 1;
function abc() {
  alert(a);
  var a = 2;
}

devolverá undefined como resultado del alert, ya que es idéntico a este código:

var a = 1;
function abc() {
  var a; /* a = undefined */
  alert(a);
  a = 2;
}

Resumen

Con estos ejemplos espero haber cubierto la base para poder avanzar con JavaScript bien programado. En los próximos posts veremos más características de JavaScript moderno. Hasta entonces, practicad los ejemplos anteriores con JsFiddle y procurad de entender la idiosincrasia del JavaScript.

JavaScript Errors in SharePoint Application Pages

Today was a day packed with investigation and troubleshooting weird JavaScript errors on a customer premise. I put this information on my blog so to help anyone who might have found in a similar journey.

SYMPTOMS

Problem 1: Views

You have a custom SharePoint 2010 master page. You assign that master page as the master page for a site, both for content and system pages. You face the following errors when dealing with list or library views:

  • You can’t change the current view in the Ribbon view selector. The combo box doesn’t drop down. Additionally, you do not have the view selector on the page title.
  • You edit the current view (or any other view). A JavaScript error is displayed in Internet Explorer and you can’t save the changes. However, in Firefox you can modify the view without any problems.
Problem 2: File Upload page

You have a custom SharePoint 2010 application page. The page inherits from the default SharePoint file upload page. You want to replace the file upload control with a custom control. However, you have to maintain the original control because the inherited code relies on the presence of those controls. You hide the controls but you still face JavaScript errors due to the hidden control validation scripts.

CAUSES

Problem 1: the culprits are the new SharePoint 2010 master page placeholders and delegate controls.

In order to have view selector working, you have to add the following DelegateControl to the master page (as found on this thread):

<SharePoint:DelegateControl runat="server" ControlId="TreeViewAndDataSource">
</SharePoint:DelegateControl>

The weird JavaScript errors on the Edit View page were caused by a PlaceHolderUtilityContent that was misplaced. This content placeholder should be placed outside the <form> tag on the master page, as this MSDN article outlines.

</form>
<asp:ContentPlaceHolder id="PlaceHolderUtilityContent" runat="server"/>

It seems that the Internet Explorer breaks when a JavaScript error is found, and skips the execution of the remaining script block while Firefox just skips the current line and executes the remaining lines if they are correct. The errors were about the objects being null, as the IE skipped the object initialization commands due to the errors generated by a misplaced placeholder tag.

Problem 2: the culprit was the JavaScript emitted by the default validation controls for the file upload selector. The solution was to embed the default control inside an ASP.NET Panel control set to Visible="false". In this way the controls are present on the server but nothing is rendered back to the page, preventing JavaScript errors.

NumberField and getElementById

Just a quick lesson learnt today in field:

SCENARIO

You want to use JavaScript to validate certain fields when the SharePoint page is in Edit Mode. You use Control.ClientID ASP.NET property to emit the control ID from the server-side code using Page.ClientScript (usually in PreRender event). You’d usually use this ID to access the control on the client side using document.getElementById.

PROBLEM

I had issues with NumberField (the server-side control that is used to render a numeric column in SharePoint). Its ClientID property is not the same as the one that is finally rendered back. In fact, it’s a child control, two levels down from the NumberField. The reason for this behaviour is that NumberField is a composite templated control, with multiple child controls that compose it.

SOLUTION

Instead of using:

numberFieldCtrl.ClientID

use

numberFieldCtrl.Controls[0].FindControl("TextField").ClientID

to get your correct control ID for JavaScript validation code on the page.

Hiding List View Group Headers

It’s really annoying that SharePoint displays those pesky column names followed by a colon when you group by a column. In this example I use the the columns “Categoria” and “Titulo” to group a bunch of pages.

image

In addition, the gray header row is ugly, too.

We can hide it with some Content Editor Web Part (CEWP) and JavaScript magic.

The offending elements can be identified:

image

We have to hide the following elements:

  • a TR element with class name ms-viewheadertr (the header row)
  • the fourth and the fifth child of a TD element with class names ms-gb (the column name of the first group by and a colon)
  • the fourth and the fifth child of a TD element with class names ms-gb2 (the column name of the second group by and a colon)

I use the excellent getElementsByClassName function developed by Robert Nyman in order to get those elements by class name.

This is the code to be pasted in a CEWP at the same page where you have the “ugly” list view. Please note that the text node which contains the colon must be removed instead of being hidden.

<script type="text/javascript" language="javascript">
_spBodyOnLoadFunctionNames.push("HideHeaders");

function HideHeaders()
{
  var elements = getElementsByClassName(document, "td", "ms-gb");
  var elem;
  for(var i=0;i<elements.length;i++)
   {
     elem = elements[i];
     elem.childNodes[3].style.display = "none";
     elem.removeChild(elem.childNodes[4]);
   }

  elements = getElementsByClassName(document, "td", "ms-gb2");

  for(var i=0;i<elements.length;i++)
   {
     elem = elements[i];
     elem.childNodes[3].style.display = "none";
     elem.removeChild(elem.childNodes[4]);
   }

  elements = getElementsByClassName(document, "tr", "ms-viewheadertr");

  for(var i=0;i<elements.length;i++)
   {
     elem = elements[i];
     elem.style.display = "none";
   }
}

/*
    Written by Jonathan Snook, http://www.snook.ca/jonathan
    Add-ons by Robert Nyman, http://www.robertnyman.com
*/
function getElementsByClassName(oElm, strTagName, strClassName){
    var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
    var arrReturnElements = new Array();
    strClassName = strClassName.replace(/-/g, "\-");
    var oRegExp = new RegExp("(^|\s)" + strClassName + "(\s|$)");
    var oElement;
    for(var i=0; i<arrElements.length; i++){
        oElement = arrElements[i];
        if(oRegExp.test(oElement.className)){
            arrReturnElements.push(oElement);
        }
    }
    return (arrReturnElements)
}
</script>

This is the result of this code:

image

Much better, isn’t it?

IE and The Mysterious ‘The page took too long to save’ Message

Another one of those “oh my god” moments in SharePoint programming, although it had nothing to do with SharePoint proper.

The Background

I want to dynamically add a DIV alongside a SharePoint field in Publishing page. The intended purpose is to extend the built-in control without any server-side code. I want the DIV to hold a simple hand-crafted AJAX call to add new items.

Everything is okay, I add my code to the SharePoint _spBodyOnLoadFunctionNames.push function, I find the control to be extended and I yank the DIV from its previous location to the parentNode object of the control. It nicely shows the new DIV below the original control:

image

The DIV contains an anchor element that has href=”#” and onclick=”SomeFunction();”. It’s created dynamically when the page loads:

var link = document.createElement("a");
link.href="#";
link.innerHTML = "Agregar nuevo elemento";   
link.onclick = "SomeFunction();";
div.parentNode.appendChild(link);

The Symptoms

When you click the anchor in the added DIV, there’s a 30-second pause during which IE responds to no clicks, after which the following message appears:

Are you sure you want to navigate away from this page?

The page took too long to save. You can click “Cancel”, and then try to save the page again. If you click “OK”, you might lose unsaved data.

Press OK to continue, or Cancel to stay away on the current page.

image

After you click OK, the anchor works well, calling the SomeFunction flawlessly.

The First Suspect

I though that the issue was caused by another function I called, that uses “AJAX” call to owssvr.dll to filter cascading dropdown controls (more on that in next posts). It might leave the HTTP request in a undefined state so that the IE thinks it’s still not loaded.

I commented it out but the error persisted.

The Second Suspect

Then I thought that it has something to do with the appendChild method I use to move the DIV from its placeholder to the new position below the control it’s extending.

I changed the call to dynamically create a new DIV instead of moving the old one to a new position in DOM tree. But, the same error persisted.

The Real Culprit

After David Gutiérrez, one of my colleagues (whose mileage in JavaScript is much greater than mine) revised the code, and after a perfunctory “brain processing” period (which lasted about an hour, in this case), he appeared and outlined the malfunction.

The error message is caused by IE trying to “execute” the new onClick event, which is set to a JavaScript function in this case. But, the syntax is wrong, wrapped inside double-quotes, like in inline HTML.

link.onclick = "SomeFunction();";

The correct syntax is written just like a method call, using only the function name:

link.onclick = SomeFunction;

IE seems to “compile” the string into a method call, but only after you confirm that you want to navigate away. Weird enough.