状況
言語選択モーダルを作っていてselectされている初期値を取得したかったのだが、外部ツールで挟んでいるため、class=selectedが描画後に追加されるので、domを監視して変更があったらそのvalueを取得したかった。
dom入れてwatchしても取れなかった。理由わからず。
template
<li v-for="(lang, index) in languages" :value="lang.code"
:ref="'lang' + index">
script
<script>
mounted() {
// 自分でdom変更して検知されるか
watchEffect(()=>{
console.log(this.$refs['lang0'])
})
},
</script>
解決策
MutationObserverという便利なものがあった。
https://developer.mozilla.org/ja/docs/Web/API/MutationObserver
<template>
<div>{{selectedLang}}</div>
<div>
<ul ref="languageList">
<li class="languageSwtich" v-for="lang in languages" :value="lang.code">
<span @click="switchLang(lang.code)">{{ lang.name }}</span>
</li>
</ul>
</div>
</template>
<script>
import { ref, onMounted, reactive } from "vue";
export default {
setup() {
const languages = reactive([
{ name: '日本語', code: 'ja' },
{ name: 'English', code: 'en' },
{ name: '中国語', code: 'zh'}
])
const selectedLang = ref('日本語')
const languageList = ref(null)
onMouned(()=>{
const observer = new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
let code = languageList.value.querySelector('.languageSwitch.selected').attributes['value']
switchLang(code)
}
}
})
// オプションchildList,subtreeをつけると子要素、孫要素まで検知できる
observer.observe(languageList.value, { attributes: true, childList: true, subtree: true})
})
const switchLang = function (code) => {
const result = languages
.filter((item) => item.code === code)
.map((item) => item.name);
selectedLang.value = result.length > 0 ? result[0] : null;
}
return {
languages,
selectedLang,
languageList,
switchLang,
}
}
}
</script>
感想
なまじ変更の検知はwatchEffectという固定観念があったせいで、無駄に時間がかかってしまった。
chatGPTに大雑把に「vueで取得したDomの中のクラスが書き換わった時にイベントを発火したい時はどうしたらいい?」と聞いたらこいつを教えてくれた、ありがとうGPT。
※追記
コメントを元に修正