【JavaScript】iframeから開いたポップアップ画面から親画面に値を渡す


親画面(別ウィンドウの呼び出し元)のiframeから別ウィンドウを立ち上げて、
その別ウィンドウに値を入力してボタンをクリックすると親画面のiframe内に値が反映される
仕組みを作りたい。と思って色々試行錯誤した時のメモ。

※ページ間の移動にはThymeleafテンプレートエンジンを使用

・環境

  • Eclipse IDE 4.9
  • SpringBoot Thymeleaf
  • Google Chrome 78

・構成

demoプロジェクト

demo/src/main/java/com/example/demo/controller/HogeController.java
demo/src/main/java/com/example/demo/DemoApplication.java
demo/src/main/resources/templates/*.html
demo/src/main/resources/static/css/*.css
demo/src/main/resources/static/js/*.js

・ソース

動作確認できたソース

index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>iframeから開いたポップアップ画面から親画面に値を渡す例</title>
<link rel="stylesheet" href="../css/bootstrap.min.css">
<script src="../js/jquery-3.4.1.min.js"></script>
<script src="../js/bootstrap.min.js"></script>
<script src="../js/main.js"></script>
</head>
<body>
    <div class="container">
        <iframe id="iframe3" name="iframeTest" width="100%" height="500"
            srcdoc="&lt;p&gt;フレーム表示される内容です。詳しくは、&lt;a href=&quot;/iframe&quot;&gt;こちらのページ&lt;/a&gt;をご確認ください。&lt;/p&gt;"
            th:src="@{/iframe}"></iframe>
    </div>
</body>
</html>

ほとんどリファレンスのパクリ
参考:http://www.htmq.com/html5/iframe.shtml

ローカルでiframe内から別ウィンドウを開く場合、呼び出し側で
iframeタグのsandbox属性を指定しないとChromeに弾かれてしまうので、
運用するならサーバー上で動かした方が良いとのこと。

chrome_devtool_console
// エラー例:
Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.

参考:https://laboradian.com/warning-about-cookie-samesite-none/

sandbox属性について
http://www.osaka-kyoiku.ac.jp/~joho/html5_ref/sandbox_attr.php?menutype=2simpll01l02l03A0

iframe.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport"
    content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>iframe内のhtml</title>
<link rel="stylesheet" href="../css/bootstrap.min.css">
<script src="../js/jquery-3.4.1.min.js"></script>
<script src="../js/bootstrap.min.js"></script>
<script src="../js/main.js"></script>
</head>
<body>
    <div class="container">
        <h5>iframe内のhtml</h5>
        <p id="iframeItem"></p>
        <p>
            <input type="button" value="ここをクリック!" onclick="popUp()" />※別ウィンドウ
        </p>
    </div>
</body>
</html>

iframeタグに使うhtml

inputForm.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport"
    content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>入力フォーム</title>
<link rel="stylesheet" href="../css/bootstrap.min.css">
<script src="../js/jquery-3.4.1.min.js"></script>
<script src="../js/bootstrap.min.js"></script>
</head>
<body>
    <div class="container">
        <!-- Content here -->
        <h3>入力フォーム</h3>
            <input type="text" class="form-control" id="inputText" />
            <button type="submit" id="trigger" class="btn btn-default">送信</button>
    </div>
    <script src="../js/iframe.js"></script>
</body>
</html>

値を入力する入力フォーム。
iframe.jsファイルを記述するscriptタグは
bodyタグの一番下に置いておかないと動かない。
恐らくページが読み込まれる前だと該当するエレメントが検出できないんだと思われるが
よくわからんので詳しい人に訊くのがヨシ!(投げやり)

main.js
// イメージポップアップ表示
function popUp() {
    w = window
            .open(
                    "/inputForm",
                    "_blank",
                    "width=600,height=400,location=yes, menubar=yes, scrollbars=yes, copyhistory=yes");
}

index.html用のポップアップウィンドウを出すためのjs

iframe.js
// iframeのDOMを取得
var btn = document.getElementById('trigger');
var elem = document.getElementById("inputText");
var target = window.opener.document.getElementById('iframeItem');

// デバッグ(ここでnullなら設定の見直しが必要)
console.log(btn);
console.log(elem);
console.log(target);

// iframe内のDOMを操作
btn.addEventListener('click', function(){
    elem = document.getElementById("inputText");
    // 入力内容が取れているか確認
    console.log(elem.value);
    target.innerHTML = elem.value;
    target.style.background="red";
    // 入力フォームのウィンドウを閉じる
    window.close();
});

本題の「iframeから開いたポップアップ画面から親画面に値を渡す」処理を記述。
以下の記事を参考にした(ほぼパクリ)

参考:javascriptでiframe内のDOM要素を取得したり操作する
https://designsupply-web.com/knowledgeside/4823/

HogeController.java
package com.example.demo.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import lombok.extern.slf4j.Slf4j;

@Scope("session")
@Controller
@Slf4j
public class HogeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index(Model model) {
        log.info("index");
        return "index";
    }

    @RequestMapping(value = "/iframe", method = RequestMethod.GET)
    public String iframe(Model model) {
        log.info("iframe");
        return "iframe";
    }

    @RequestMapping(value = "/inputForm", method = RequestMethod.GET)
    public String inputForm(Model model) {
        log.info("inputForm");
        return "inputForm";
    }
}

何の変哲もないただのThymeleaf用のコントローラ

・起動して確認

DemoApplication.javaを実行
Chromeで http://localhost:8080/ にアクセスする。
ウィンドウで「F12」を押すと開発者ツールが起動するのでそこでデバッグ、確認できる。
うまく値が渡れば成功!