小さい頃はエラ呼吸

いつのまにやら肺で呼吸をしています。


子画面から親画面に値を返したときにonchangeイベントが発生しない件

はじめに

このエントリでは、親子関係のある画面(ウインドウ)で、子画面から親画面に対して値を返した際に、onchangeイベントが発生しない問題とその対処方法について説明します。

子画面から親画面に値を返したときにonchangeイベントが発生しない

以下のサンプルコードでは、親子関係のある画面において、子画面から親画面の特定の要素の値を変更することで、値を返却しようとしています。ですが、この場合、alert("子画面から戻り値が返りました。");の命令が呼びされません。親画面側で値が変わった際のonchangeイベントが発生しないのです。

親画面(HTML)
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>親ウインドウ</title>
<script type="text/javascript" src="parentWindow.js" charset="UTF-8"></script>
</head>
<body>
<h1>親ウインドウ</h1>
<input type="button" id="openChild" name="openChild" value="子ウインドウを表示する">
<input type="text" id="retValue" name="retValue">
</body>
</html>
親画面(JavaScript)
var childWindow; // 子ウインドウオブジェクト
onload = function() {
  document.getElementById("openChild").onclick = function() {
    childWindow = open("childWindow.html", "", "");
  }

  document.getElementById("retValue").onchange = function() {
    alert("子画面から戻り値が返りました。");
  }
}
子画面(HTML)
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>子ウインドウ</title>
<script type="text/javascript" src="childWindow.js" charset="UTF-8"></script>
</head>
<body>
<h1>子ウインドウ</h1>
<input type="button" id="returnValue" name="returnValue" value="親ウインドウに値を返す">
</body>
</html>
子画面(JavaScript)
onload = function() {
  // 親ウインドウに値を返す
  document.getElementById("returnValue").onclick = function() {
    if (opener) {
      opener.document.getElementById("retValue").value = "hoge";
      close();
    }
  }
}
JavaScriptで値を変えた場合onchangeイベントは発生しない

onchangeイベントが発生しない理由は、以下のサイトに書かれています。テキストボックスやhiddenタグは、JavaScriptで値を変更してもonchangeイベントが発生しないようです。

javascript を使用してテキストボックスの内容をプログラムから変更しても onchange イベントは発生しません。
javascript の textbox の onchange イベントが発生しない - Windows Live はてなブックマーク - javascript の textbox の onchange イベントが発生しない - Windows Live

任意のイベントを強制的に発生させる

この問題を回避するには、子画面から親画面のonchangeイベントを強制的に発生させます。具体的には、fireEventというメソッドを使います。

恥ずかしながら知らなかったのですが、fireEventというメソッドで指定オブジェクトのイベントを発生させられるようです。
object.fireEvent('eventName')
JavaScriptで任意のオブジェクトのイベントを発生させる - shin5papaの日記 はてなブックマーク - JavaScriptで任意のオブジェクトのイベントを発生させる - shin5papaの日記

さきほどの子画面のJavaScriptを以下のようにすると、親画面の値が変更されたタイミングで、アラートが表示されます。

onload = function() {
  // 親ウインドウに値を返す
  document.getElementById("returnValue").onclick = function() {
    if (opener) {
      opener.document.getElementById("retValue").value = "hoge";
      // 親ウインドウのonchangeイベントを強制的に発生させる
      opener.document.getElementById("retValue").fireEvent("onchange");
      close();
    }
  }
}