👌

Vue+compositionAPIでiframe内のスクロールを検知する

2021/12/08に公開

はじめに

webアプリでよく見る利用規約を下までスクロールするとボタンが活性化し、同意できる機能を実装したい

コード全体

<template>
  <div class="container">
    <div class="content">
      <div class="term-of-use-container">
        <iframe
          class="term-of-use"
          ref="termOfUseRef"
          src="/term_of_use.html"
          frameborder="0"
        />
      </div>
      <div class="button-container">
        <button
          class="button color-orange"
          :class="{ 'color-gray': isDisabled }"
          :disabled="isDisabled"
          @click="onClickButton"
        >
          同意する
        </button>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from "vue";

export default defineComponent({
  setup() {
    const termOfUseRef = ref<HTMLIFrameElement>();
    const isDisabled = ref<boolean>(true);

    const onClickButton = () => alert("同意しました!");

    onMounted(() => {
      termOfUseRef.value?.contentWindow?.addEventListener("scroll", () => {
        if (!termOfUseRef.value) return;
        if (!termOfUseRef.value.contentWindow) return;
        const scrollAdjustmentValue = 50;
        // テキスト全体の高さ
        const scrollHeight =
          termOfUseRef.value.contentDocument?.documentElement.scrollHeight || 0;
        // スクロール量
        const scrollAmount = termOfUseRef.value.contentWindow.scrollY || 0;
        // ウィンドウ(表示される枠)の高さ
        const windowHeight = termOfUseRef.value.contentWindow.innerHeight || 0;

        if (
          scrollAmount + windowHeight + scrollAdjustmentValue >
          scrollHeight
        ) {
          isDisabled.value = false;
        }
      });
    });

    return {
      termOfUseRef,
      isDisabled,
      onClickButton,
    };
  },
});
</script>
<style scoped>
.container {
  display: flex;
  justify-content: center;
  width: 100vw;
  height: 100vh;
}
.content {
  margin-top: 50px;
  margin-bottom: 50px;
}
.term-of-use {
  width: 500px;
  height: 500px;
}
.button-container {
  display: flex;
  justify-content: center;
  margin-top: 20px;
}
.button {
  width: 200px;
  color: #fff;
  border-radius: 100vh;
}
.color-orange {
  background-color: orange;
}
.color-gray {
  background-color: gray;
}
</style>

解説

<iframe
  class="term-of-use"
  ref="termOfUseRef"
  src="/term_of_use.html"
  frameborder="0"
/>

vueテンプレート内にref="termOfUseRef"のようにテンプレート参照を付与します。

const termOfUseRef = ref<HTMLIFrameElement>();

setup関数内で参照の変数を定義します。このとき変数名はテンプレート内に記述したref="termOfUseRef"と同じ名前にします。

onMounted(() => {
  termOfUseRef.value?.contentWindow?.addEventListener("scroll", () => {
    // 省略
  });
});

onMounted内でイベントリスナを呼び出しスクロールイベントを検知します。
ポイントはcontentWindowの部分で、これによりiframe内のDOMにアクセスすることができます。そこでイベントリスナを呼び出すことでiframe内のスクロールを検知することができます。

https://developer.mozilla.org/ja/docs/Web/API/HTMLIFrameElement/contentWindow

const scrollAdjustmentValue = 50;
// テキスト全体の高さ
const scrollHeight =
  termOfUseRef.value.contentDocument?.documentElement.scrollHeight || 0;
// スクロール量
const scrollAmount = termOfUseRef.value.contentWindow.scrollY || 0;
// ウィンドウ(表示される枠)の高さ
const windowHeight = termOfUseRef.value.contentWindow.innerHeight || 0;

if (
  scrollAmount + windowHeight + scrollAdjustmentValue >
  scrollHeight
) {
  isDisabled.value = false;
}

最後にこちらのコードでスクロール量を計算し、下までスクロールした時にボタン活性化のフラグを更新します。

参考

https://qiita.com/sinsia/items/54865025821cb9203cfc

Discussion