IE8以下で、'{window} click'にバインドした時に、イベントハンドラの第2引数がwindowと===にならない #339

Closed
fukudayasuo opened this Issue Jun 4, 2014 · 3 comments

Comments

Projects
None yet
2 participants
@fukudayasuo

コントローラでイベントハンドラをバインドした時、第2引数にはそのイベントハンドラのcurrentTargetのjQueryオブジェクトが渡される。

'{window} click'にバインドしたハンドラの第2引数には、$(window)が渡されるはずであるが、以下のようなコードで比較した時、$elの中身がwindowと===にならない。

h5.core.controller('body', {
  __name:'A',
  '{window} click': function(context, $el) {
    $el[0] === window; // falseになる。
  }
});

IE8でjQuery1.10.0で確認した。

@fukudayasuo

This comment has been minimized.

Show comment
Hide comment
@fukudayasuo

fukudayasuo Jun 4, 2014

原因は、IE8-でwindowオブジェクトを比較するときの挙動に起因している。

コントローラはイベントハンドラのセレクタが'{window}'だった時には、そのコントローラのルートエレメントが属しているdocumentのparentWindow(IE8-の場合)にバインドしている。

$elは、イベントハンドラのthisをjQueryオブジェクトにしたものである。なので、$elは$(document.parentWindow)である。

IE9以上では、document.parentWindow === window であるが、IE8-では、document.parentWindow !=== windowである。

以下にIE8(IE10のdocmode8)で、windowオブジェクトの比較をテストした結果を示す。(7の結果以外は参考まで。)

var window1 = window;
window2 = window;
window.window3 = window;
var obj = {
        w: window
};
window.obj = obj;
window.obj.w2 = window;

window === window.window; // 1
window === window1;  // 2
window === window2; // 3
window !== window3; // 4
window === window.obj.w; // 5
window === window.obj.w2; // 6
document.parentWindow !== window; // 7
document.parentWindow !== window2; // 8
document.parentWindow === window3; // 9

本issueで問題となっているのは、7の比較が!==になることである。

!==ではあるものの、インスタンスは同一であるような挙動をし、例えばwindow.a = 1;とすると、document.parentWindow.a === 1になり、逆にdocument.parentWIndowにプロパティを追加した場合も同様である。

そのため、イベントハンドラの第2引数は、windowオブジェクトの操作を行う分には特に問題は発生しない。windowと比較して!==になってしまうことだけが問題である。

また、ポップアップウィンドウやiframeなど、別ウィンドウを参照する分にはparentWindowとwindowオブジェクトは===になる。

popupWindow = window.open();
// ポップアップが開いた後
popupWindow === popupWindow.document.parentWindow;

しかし、ポップアップウィンドウ内のスクリプトで、windowを比較した時には、同様の問題が発生する。

// ポップアップウィンドウ内のスクリプト
document.parentWindow !== window;
window.opener.popupWin !== window;
window.opener.popupWin === document.parentWindow;
window.opener === window.opener.document.parentWindow;

FWの対応としては以下の候補がある。

  • windowと"==="で比較してtrueになるように、windowにバインドするときは、document.parentWindowではなくwindowにバインドする(ただし、コントローラのルートエレメントが自window内の時のみ)
    (getWindowOfDocument(doc)を、window.document===docならwindowを返すようにする)
  • FWは対応しない。("==="で比較できないだけで、windowオブジェクトを使用する分には問題ないため。)
    テストケースでも"==="で比較することはできないので、テストケースでは何らかの対応を行う。

原因は、IE8-でwindowオブジェクトを比較するときの挙動に起因している。

コントローラはイベントハンドラのセレクタが'{window}'だった時には、そのコントローラのルートエレメントが属しているdocumentのparentWindow(IE8-の場合)にバインドしている。

$elは、イベントハンドラのthisをjQueryオブジェクトにしたものである。なので、$elは$(document.parentWindow)である。

IE9以上では、document.parentWindow === window であるが、IE8-では、document.parentWindow !=== windowである。

以下にIE8(IE10のdocmode8)で、windowオブジェクトの比較をテストした結果を示す。(7の結果以外は参考まで。)

var window1 = window;
window2 = window;
window.window3 = window;
var obj = {
        w: window
};
window.obj = obj;
window.obj.w2 = window;

window === window.window; // 1
window === window1;  // 2
window === window2; // 3
window !== window3; // 4
window === window.obj.w; // 5
window === window.obj.w2; // 6
document.parentWindow !== window; // 7
document.parentWindow !== window2; // 8
document.parentWindow === window3; // 9

本issueで問題となっているのは、7の比較が!==になることである。

!==ではあるものの、インスタンスは同一であるような挙動をし、例えばwindow.a = 1;とすると、document.parentWindow.a === 1になり、逆にdocument.parentWIndowにプロパティを追加した場合も同様である。

そのため、イベントハンドラの第2引数は、windowオブジェクトの操作を行う分には特に問題は発生しない。windowと比較して!==になってしまうことだけが問題である。

また、ポップアップウィンドウやiframeなど、別ウィンドウを参照する分にはparentWindowとwindowオブジェクトは===になる。

popupWindow = window.open();
// ポップアップが開いた後
popupWindow === popupWindow.document.parentWindow;

しかし、ポップアップウィンドウ内のスクリプトで、windowを比較した時には、同様の問題が発生する。

// ポップアップウィンドウ内のスクリプト
document.parentWindow !== window;
window.opener.popupWin !== window;
window.opener.popupWin === document.parentWindow;
window.opener === window.opener.document.parentWindow;

FWの対応としては以下の候補がある。

  • windowと"==="で比較してtrueになるように、windowにバインドするときは、document.parentWindowではなくwindowにバインドする(ただし、コントローラのルートエレメントが自window内の時のみ)
    (getWindowOfDocument(doc)を、window.document===docならwindowを返すようにする)
  • FWは対応しない。("==="で比較できないだけで、windowオブジェクトを使用する分には問題ないため。)
    テストケースでも"==="で比較することはできないので、テストケースでは何らかの対応を行う。
@fukudayasuo

This comment has been minimized.

Show comment
Hide comment
@fukudayasuo

fukudayasuo Jun 6, 2014

バインドするターゲットを、window.document.parentWindowではなく、windowにバインドするようにして対応する。

'{window}'にバインドするとき、コントローラのルートエレメントからdocumentを取得して、取得したdocumentからwindowオブジェクトをgetWindowOfDocument(doc)を使って取得している。

このgetWindowOfDocument(doc)を修正する。今のコードだと、(doc.defaultView || doc.parentWIndow)を返しているが、window.document === docかどうかを比較してtrueならparentWindowではなくwindowを返すように修正する。

バインドするターゲットを、window.document.parentWindowではなく、windowにバインドするようにして対応する。

'{window}'にバインドするとき、コントローラのルートエレメントからdocumentを取得して、取得したdocumentからwindowオブジェクトをgetWindowOfDocument(doc)を使って取得している。

このgetWindowOfDocument(doc)を修正する。今のコードだと、(doc.defaultView || doc.parentWIndow)を返しているが、window.document === docかどうかを比較してtrueならparentWindowではなくwindowを返すように修正する。

@simdy simdy added the bug label Jun 8, 2014

@simdy simdy added this to the v1.1.11 milestone Jun 8, 2014

@fukudayasuo

This comment has been minimized.

Show comment
Hide comment
@fukudayasuo

fukudayasuo Jun 9, 2014

IE10のdocumode8,7ではなく、本物のIE8,IE7で確認したところ、
window.window !== windowでした。

コントローラのバインド先に'{window}'が指定されていた場合は、バインド先の取得にgetByPathを使用していてwindow.windowを取得しています。

そのため、getWindowOfDocumentを修正しても、'{window}'をバインドするときのバインドターゲットはwindowではなくwindow.windowになってしまいます。

getByPathも修正します。

具体的には、getByPath(namespace, rootObj)で、rootObj===window(またはrootObjの指定が無い)且つ、namespaceが"window."で始まっているなら、最初の"window."は無視するようにします。

IE10のdocumode8,7ではなく、本物のIE8,IE7で確認したところ、
window.window !== windowでした。

コントローラのバインド先に'{window}'が指定されていた場合は、バインド先の取得にgetByPathを使用していてwindow.windowを取得しています。

そのため、getWindowOfDocumentを修正しても、'{window}'をバインドするときのバインドターゲットはwindowではなくwindow.windowになってしまいます。

getByPathも修正します。

具体的には、getByPath(namespace, rootObj)で、rootObj===window(またはrootObjの指定が無い)且つ、namespaceが"window."で始まっているなら、最初の"window."は無視するようにします。

fukudayasuo pushed a commit to hifive-labs/hifivemain that referenced this issue Jun 9, 2014

fukudayasuo
#339 getWindowOfDocument(doc)を、doc===window.documentならwindowを返すようにしました。
getByPathを、rootObjがwindowまたは指定無しなら、namespaceの最初の'window.'を無視するようにしました。

テストケース追加
'{window}'にバインドした時に第2引数に$(window)が来ることを確認するアサートのコメントアウトを外しました。

@simdy simdy closed this Jun 9, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment