2012-05-08 45 views
9

Tôi có hai cửa sổ, một cửa sổ được mở từ cửa sổ khác, vì vậy, tôi có thuộc tính opener trong cửa sổ "con".Không thể chuyển chức năng từ cửa sổ này sang cửa sổ khác trong IE

Cửa sổ chính có một số chức năng trong phạm vi toàn cục, phải được gọi bằng hàm làm đối số đầu tiên (nó sẽ được sử dụng làm gọi lại).

Cả hai trang được mở từ cùng một tên miền, như vậy, tôi không có bất kỳ hạn chế Policy xứ Same (Tôi hy vọng như vậy) ...

Trong một cửa sổ con tôi có mã như thế này

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

Mọi thứ đều hoạt động tốt, cho đến khi tôi thử chạy nó trong IE. Trong trình duyệt này, một đối số, được nhận bởi myFunction, là LUÔN loại Object (được kiểm tra với typeof). Mã của myFunction là một cái gì đó như thế này:

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

Live Demo: http://elifantiev.ru/ie-opener-issue/first.html

Các câu hỏi là:

  1. là này một standarts hành vi phù hợp?
  2. có một số giải pháp cho sự cố này không?
+0

Vui lòng chỉ định (các) phiên bản IE mà bạn gặp sự cố này. –

+0

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

Trả lời

6

Mặc dù typeof trả về "đối tượng" nhưng hàm vẫn hoạt động như mong đợi. Gọi cb() sẽ thực hiện chức năng.

Một thay thế cho việc sử dụng typeof để xác định nếu tham số là một chức năng đang thử nghiệm cho các tài sản gọi đó tất cả các chức năng được dự kiến ​​sẽ có:

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

Nếu bạn đang đi dọc theo một cái gì đó mà hy vọng một hàm nó có thể được bọc như thế này:

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

Cũng lưu ý khi truyền chức năng từ cha mẹ sang con, typeof cũng là đối tượng. So sánh hàm gốc với hàm-as-an-object (sau chuyến đi khứ hồi) trả về giá trị true. Điều này quan trọng nếu vì một lý do nào đó bạn cần tham chiếu đến hàm ban đầu (ví dụ như khi hủy đăng ký).

+0

+1 cho 'if (cb && cb.call)' ... tốt nhất để tránh xa 'typeof' –

+0

Gói là đẹp, nhưng không thể được sử dụng, gây ra 'tôi cần phải thêm gói vào bất kỳ chức năng nào của khung công tác, có thể nhận chức năng như một đối số ... – Olegas

+0

Tôi không thích chúng nhưng bạn có thể làm mẫu hàm để bạn không phải lo lắng về việc viết lại toàn bộ khung công tác của mình, chỉ với khách hàng của bạn các cuộc gọi bên cạnh. Tôi sẽ cập nhật câu trả lời của tôi sau một phút. – Jeff

2

Dường như đây là theo thiết kế.

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

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

Bởi vì mỗi cửa sổ có thể có chuỗi nguyên mẫu khác nhau, chức năng không thể được thông qua như mong đợi.Hãy thử làm một cái gì đó như thế này để khẳng định:

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

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

Vì vậy, dưới đây là cách tôi sẽ làm đúng đắn xác nhận chức năng nếu có nhiều chức năng để lo lắng về việc từ phía cha mẹ (Tôi ghét nguyên mẫu nhưng lần này tôi nghĩ rằng bạn đang mắc kẹt) :

Chánh Window:

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

Child Window:

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

Và đây là ví dụ làm việc: http://dl.dropbox.com/u/169857/first.html

-1

Sau khi mở cửa sổ con, bạn có thể đăng ký chức năng do cha mẹ tạo thành ngữ cảnh window. Bạn có thể xem đoạn mã sau chạy trong 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

Vấn đề chính là cách chuyển một hàm, được tạo trong cửa sổ con, làm đối số cho hàm cửa sổ của cha mẹ. – Olegas

+0

@Olegas, bài đăng gốc của bạn cho biết: "Cửa sổ chính có một số chức năng trong phạm vi toàn cục, phải được gọi bằng hàm làm đối số đầu tiên (nó sẽ được sử dụng làm gọi lại)." Vì vậy, tôi mong đợi vấn đề được gọi là một chức năng được xác định trong cửa sổ cha mẹ từ cửa sổ con. Chuyển một hàm trong Javascript, cho các vấn đề thực tế cũng giống như truyền bất kỳ đối số nào khác, và đó là lý do tại sao tôi không khám phá nó. Nhưng không bao giờ, có vẻ như bạn đã giải quyết được vấn đề của mình mà bạn không còn giúp đỡ gì thêm. –

+0

Để gọi - không phải là một vấn đề. Để vượt qua một chức năng - là vấn đề. Trong IE, nó đang thay đổi 'typeof' thành' Object' khi đi qua ranh giới cửa sổ. – Olegas

2

Thậm chí nếu bạn có thể có được điều này để làm việc cho bây giờ, tôi sẽ không tin tưởng vào khả năng cho hai cửa sổ trình duyệt để trực tiếp gọi chức năng vào nhau là xung quanh lâu hơn nữa. Chỉ có quá nhiều mối quan tâm về bảo mật, ngay cả khi bạn xóa các vấn đề về miền chéo khỏi kịch bản.

Cách duy nhất trong tương lai để làm điều này và đảm bảo rằng nó hoạt động trên tất cả các trình duyệt, là sử dụng cross-document messaging APIs được hỗ trợ trong tất cả các trình duyệt hiện đại, bao gồm IE8 trở lên.

Ví dụ chi tiết nhất mà tôi có thể tìm thấy khi tôi cần giải quyết vấn đề này là window.postMessage article on MDN.

gì nó boils xuống đến là một cuộc gọi ở một bên để gửi tin nhắn, ví dụ:

otherWindow.postMessage(message, targetOrigin); 

Và sau đó một event handler ở phía bên kia:

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

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

IE6 và IE7 sức mạnh cho phép bạn thực hiện các cuộc gọi qua cửa sổ ngay bây giờ nếu các cửa sổ từ cùng một miền, nhưng IE8 trở lên có khả năng sẽ sử dụng API này và tôi đoán các trình duyệt khác cuối cùng sẽ hoàn nguyên trở lại giao tiếp an toàn hơn nhiều này cơ chế.

Trong trường hợp này, tôi khuyên bạn nên sử dụng thư viện dùng chung để chia sẻ mã của bạn, (sử dụng một cái gì đó như LAB.js hoặc require.js hoặc thậm chí chỉ là hàm getScript() của Jquery, và sau đó sử dụng tính năng nhắn tin chéo hệ thống để gửi các sự kiện, thay vì chức năng gọi lại bạn đang cố gắng sử dụng ngày hôm nay.

CẬP NHẬT


Có polyfills sẵn để hỗ trợ thêm postMessage cho các trình duyệt cũ. Vì bạn không thực hiện cuộc gọi tên miền chéo, tôi sẽ bắt đầu với số Jquery postMessage plugin của Ben Alman. Tôi đã không sử dụng nó, nhưng tôi đã sử dụng các plugin Jquery BBQ tuyệt vời của Ben. Nó sẽ quay trở lại các phương thức tích hợp nếu được hỗ trợ và chỉ sử dụng các phương pháp thay thế trong các trình duyệt cũ hơn. Nó tuyên bố để làm việc trong IE7 ...

Nếu plugin của Ben không thực hiện thủ thuật, thì bạn có thể thử easyXDM, nhưng có vẻ phức tạp hơn một chút để thiết lập.

+0

Tôi nghĩ rằng đây là một workaraound phù hợp nhất, standart. Nhưng đây là một vấn đề đối với tôi những gì nó không được hỗ trợ trong IE7. Dù sao, tôi nghĩ bạn là người chiến thắng. – Olegas

+0

Tôi không nghĩ đến việc xem xét trước (tôi đã chống lại thời hạn), nhưng có một số polyfills để cung cấp phương thức này trong IE7. Tôi đã cập nhật câu trả lời để đưa họ vào. –

+0

Đẹp lib, nhưng nó sử dụng location.hash để truyền thông điệp ... Trong môi trường băm của tôi là rất cao được sử dụng cho nhu cầu nội bộ (như chuyển hướng, vv ...). Nhưng nó có thể được sửa đổi để đáp ứng nhu cầu của tôi và không phá vỡ bất cứ thứ gì đang hoạt động. Cám ơn. – Olegas

Các vấn đề liên quan