2012-05-08 9 views
9

Tengo dos ventanas, una se abre desde otra, entonces, tengo una propiedad opener en la ventana "secundaria".No se puede pasar una función de una ventana a otra en IE

La ventana principal tiene alguna función en alcance global, que se debe invocar con una función como primer argumento (se utilizará como una devolución de llamada).

Ambas páginas se abren desde mismo dominio, así, no tiene ninguna restricción Política mismo origen (eso espero) ...

En una ventana secundaria que tengo un código como éste

if(window.opener) { 
    window.opener.myFunction(function() { ... }); 
} 

Todo funciona bien, hasta que intento ejecutarlo en IE. En este navegador, un argumento, recibido por myFunction, SIEMPRE es del tipo Object (marcado con typeof). Código de myFunction es algo como esto:

window.myFunction = function(cb) { 
    alert('Callback type is ' + (typeof cb)); 
    if(typeof cb == 'function') 
     cb(); 
    else 
     alert('Not a function!'); 
} 

Demostración en directo: http://elifantiev.ru/ie-opener-issue/first.html

las preguntas es:

  1. ¿Es esta una estandares de conducta de cumplimiento?
  2. ¿hay alguna solución para este problema?
+0

Especifique la versión (es) de IE en la que encuentra este problema. –

+0

IE7 + (IE 7,8,9) – Olegas

Respuesta

6

Aunque typeof devuelve "objeto" la función sigue funcionando como se esperaba. Llamar al cb() ejecutará la función.

Una alternativa al uso typeof para determinar si el parámetro es una función está probando para la propiedad llamada que se espera que todas las funciones que tiene:

if (cb && cb.call) { cb(); } 

Si estás pasando junto a algo que espera una función que puede ser envuelto de esta manera:

function newCb() { 
    return cb.apply(object, arguments); 
} 

también tenga en cuenta cuando se pasa una función de padre a hijo del typeof también se opone. La comparación de la función original con la función-como-un-objeto (después del viaje de ida y vuelta) devuelve verdadero. Esto es importante si por alguna razón necesita una referencia a la función original (por ejemplo, al cancelar la suscripción).

+0

+1 para 'if (cb && cb.call)' ... lo mejor es mantenerse alejado de 'typeof' –

+0

El envoltorio es agradable, pero no se puede usar, porque 'I Necesito agregar el ajuste a cualquier función de framework, que puede recibir una función como argumento ... – Olegas

+0

No me gustan pero puedes prototipar Function para que no tengas que preocuparte de reescribir todo tu framework, solo tu cliente llamadas de lado Actualizaré mi respuesta en un minuto. – Jeff

2

Parece que esto es por diseño.

http://www.kilometer0.com/blog/code/internet-explorer-ie-cross-window-javascript-object-typeof-bug/

https://bugzilla.mozilla.org/show_bug.cgi?id=475038

Debido a que cada ventana puede tener diferentes cadenas de prototipos, las funciones que no se pueden pasar como se esperaba.Trate de hacer algo como esto para confirmar:

var popup = window.open('second.html'); 

    window.myFunction = function(cb) { 
      alert(cb instanceof Function);  //false 
      alert(cb instanceof popup.Function); //true 
    } 

Así que aquí es cómo lo haría la función de validación adecuada si hubo numerosas funciones de las que preocuparse en el lado de los padres (odio prototipo, pero esta vez creo que le pegan) :

ventana

Padres:

<html> 
<script type="text/javascript"> 
    /* give all our functions the validation check at once */ 
    Function.prototype.remote = function(cb) { 
     if(cb instanceof popup.Function) { 
      this(cb); 
     } else { 
      alert('Not a function!'); 
     } 
    }; 

    var popup, 
    myFunction = function(cb) { 
     cb(); 
    }, 
    test = function(cb) { 
     cb(); 
    } 

</script> 
<body> 
    <a href="#" onclick="popup = window.open('second.html'); return false;">Open window</a> 
</body> 
</html> 

ventana niño:

<html> 
<body> 
    <script type="text/javascript"> 
     var Parent = window.opener; 
     if(Parent) { 
      Parent.myFunction.remote(function(){ 
       alert('callback!'); 
      }); 

      Parent.test.remote(function() { 
       alert('callback again'); 
      }); 
     } 
    </script> 
    This is a second page! 
</body> 
</html> 

Y aquí hay un ejemplo de trabajo: http://dl.dropbox.com/u/169857/first.html

-1

Después de abrir la ventana secundaria, puede registrar una función creada por el padre en su contexto window. Se puede ver el siguiente código se ejecuta en jsFiddle:

<html> 
<head> 
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> 
<script> 

// will be called by the child window 
function MyExportedCallback(x) { 
    $("#my-callback-result").text("this callback was run with '" + x + "'."); 
} 

// will open a child window and create content for testing 
function MyLinkHandler() { 
    var wnd = window.open("about:blank"); 

    // IMPORTANT: this is where the parent defined function is "registered" 
    // into child window scope 
    wnd["myExternal"] = MyExportedCallback; 

    // auxiliary code to generate some HTML to test the external function 
    var put = function(x) { wnd.document.write(x); }; 
    put("<html><body>"); 
    put("<a href='#' onclick='window.myExternal(123);'>"); 
    put("click me to run external function"); 
    put("</a>"); 
    put("</body></html>"); 
} 

// attach events 
$.ready(function() { 
    $("#my-link").click(MyLinkHandler); 
}); 

</script> 
<body> 
<p><a id="my-link" href="#">Open window</a></p> 
<p id="my-callback-result">waitting callback...</p> 
</body> 
</html> 
+0

El problema principal es cómo pasar una función, creada en la ventana secundaria, como un argumento para la función de ventana principal. – Olegas

+0

@Olegas, su publicación original dice: "La ventana primaria tiene alguna función en el ámbito global, que debe invocarse con una función como primer argumento (se utilizará como devolución de llamada)". Así que esperaba que el problema llamara a una función definida en la ventana principal desde la ventana secundaria. Pasar una función en Javascript, por cuestiones prácticas es como pasar cualquier otro argumento, y es por eso que no lo exploré. Pero no importa, parece que ya has resuelto tu problema y ya no necesitas ayuda. –

+0

Llamar, no es un problema. Pasar una función - es el problema. En IE está cambiando su 'typeof' a' Object' cuando pasa a través del límite de la ventana. – Olegas

2

Incluso si usted puede conseguir que esto funcione, por ahora, yo no contaría con la posibilidad de que dos ventanas del navegador para invocar directamente las funciones unos de otros para estar alrededor mucho mas largo. Hay demasiadas preocupaciones de seguridad, incluso si elimina los problemas de dominio cruzado del escenario.

La única forma de hacerlo a prueba del futuro, y asegurarse de que funciona en todos los navegadores, es usar cross-document messaging APIs compatibles con todos los navegadores modernos, incluido IE8 y superior.

El ejemplo más detallado que pude encontrar cuando necesité resolver este problema fue este window.postMessage article on MDN.

Lo que se reduce a es una llamada de un lado para enviar el mensaje, por ejemplo:

otherWindow.postMessage(message, targetOrigin); 

Y a continuación, un controlador de eventos en el otro lado:

window.addEventListener("message", receiveMessage, false); 

function receiveMessage(event) 
{ 
    alert("I got it!\n" + event.data); 
} 

IE6 y IE7 podría permitirte hacer esas llamadas de ventana cruzada por ahora si las ventanas son del mismo dominio, pero es probable que IE8 y superiores esperen que uses esta API, y supongo que otros navegadores finalmente volverán completamente a esta comunicación mucho más segura mecanismo.

Dada la situación, recomiendo usar una biblioteca compartida para compartir tu código (usando algo como LAB.js o require.js o incluso solo la función getScript() de Jquery), y luego usar la mensajería cruzada de documentos sistema para enviar los eventos, en lugar de la función de devolución de llamada que está tratando de usar hoy.

ACTUALIZACIÓN


Hay polyfills disponibles para añadir soporte postMessage para navegadores antiguos. Como no está haciendo llamadas entre dominios, comenzaría con el Jquery postMessage plugin de Ben Alman. No lo he usado, pero he usado los excelentes complementos Jquery BBQ de Ben. Debería recurrir a los métodos integrados si es compatible, y solo usar métodos alternativos en navegadores antiguos. Afirma que funciona en IE7 ...

Si el complemento de Ben no funciona, entonces podrías probar con easyXDM, pero parece un poco complicado para configurarlo.

+0

Creo que este es el mejor entorno de trabajo compatible con estándares. Pero este es un problema para mí que no es compatible con IE7. De todos modos, creo que eres el ganador. – Olegas

+0

No pensé en mirar antes (estaba en contra del plazo), pero hay algunos polyfills para proporcionar este método en IE7. Actualicé la respuesta para incluirlos. –

+0

Nice lib, pero usa location.hash para pasar mensajes ... En mi entorno, el hash se usa mucho para necesidades internas (como navegación, etc.). Pero se puede modificar para satisfacer mis necesidades y no romper nada de lo que ya está funcionando. Gracias. – Olegas

Cuestiones relacionadas