半年間の長期インターンを始めたので、1ヶ月ごとに学んだこととかを吐き出してみるの会【11月編】
はじめに
今月は、月末に実装したスクロール関係の実装についてと、NuxtにおけるCtrl+クリック or Cmd+クリックした際に新規タブでページを開く実装についてになります。
子コンポーネントにおけるスクロールがややこしかった件
Webアプリケーションで特定ページへの遷移後、ブラウザバックした際にスクロール位置を復元する機能を実装する機会があり、スクロール位置の取得や挙動制御で予想以上に苦労したため、苦労した点と最終的な実装内容について、書いていこうと思います。
window.scrollY
で取得できなくない?
概要は前述の通りで、まず初めにCookieにスクロール位置を保存・取得する実装から始めたのですが、早くもここで問題が発生。
window.scrollY
でスクロール位置が全く取得できないではないか、、
さらには、
window.screenY
でも取得できないではないか、、、
どちらも、数値の取得はできるのですが
- スクロールしているのに、常に0が返却される
- スクロールしていないと0、スクロールすると位置に関係なく、25が返却される
など、まともにスクロール位置が取得できずにいました。
当初は、window.scrollY
を試してみたものの、期待通りに動作せず。最終的にわかった原因としては、window.scrollY
は(おそらく、仕様上)親コンポーネント上でのスクロール位置を取得してしまう仕様になっているため、固定サイドバーを実装している場合は、サイドバーの方のスクロール位置を取得してしまっていたようです。
ちなみにですが、ダメ元でwindow.screenY
を使ってスクロール位置を取得しようと試みていたのですが、これは「モニター画面上でのブラウザウィンドウの位置」を取得するためのプロパティだったため、当たり前のように取得はできませんでした。
結論として、子コンポーネントでスクロール位置を正確に取得したい場合は、element.scrollTop
を使用する必要があるぽいです。scrollTop
は、特定の要素を指定することで、指定した要素を基準にスクロール位置を取得するため、子コンポーネントのスクロール位置を取得する場合は、こちらを利用するとちゃんと取得できました。
ということで、今回実装したコードになります
<script setup lang="ts">
// スクロール位置の記憶系
const scrollPositionCookie = useCookie(`channelScrollPosition`)
const scrollContainer = ref<HTMLElement | null>(null)
const scrollTop = ref(0)
onMounted(async () => {
// cookieからスクロール位置を読み出して、その位置までスクロール
if (scrollPositionCookie.value && scrollContainer.value) {
nextTick(() => {
scrollContainer.value?.scrollTo({
top: parseInt(scrollPositionCookie.value),
behavior: 'instant'
})
})
}
scrollContainer.value?.addEventListener('scroll', handleScroll)
})
// スクロール位置を監視
const handleScroll = (event: Event) => {
const target = event.target as HTMLElement
scrollPositionCookie.value = target.scrollTop.toString()
}
onUnmounted(() => {
scrollContainer.value?.removeEventListener('scroll', handleScroll)
})
</script>
<template>
<div ref="scrollContainer"></div>
</template>
Nuxt3でCtrlもしはCmdを押しながらクリックしたら新規タブで開きたい
Nuxt.jsでは、リンクを埋め込む方法としては主に3つあります。
- useRouter().push()
- navigationTo
- NuxtLink
の3つですね。
この中で、唯一新規タブで開く動作ができるのは、NuxtLinkタグのみになります。
NuxtLinkの場合は、
<NuxtLink target="_blank"></NuxtLink>
のように書くことで、新しいタブで開くことができます。
そのため、Ctrl
or Cmd
が押されている場合は、動的に属性を追加するという方法も可能ではあると思います(試していない)
が、今回の場合、カード全体に当たり判定を作りたいということもあり、別の方法(window.open
)を使って実装しました。
ロジックとしては、
-
useRouter().resolve(to).fullPath
で、開く先のリンクのフルパスを取得 -
Ctrl
orCmd
が押されているかどうかを判定 -
window.open()
を使って、Ctrl
orCmd
が押されている場合は_blank
属性を追加する
という流れで実装しました。
const onClickEvent = (pathTo: string, event?: MouseEvent) => {
const fullPathTo = router.resolve(pathTo).fullPath
if (event && (event.ctrlKey || event.metaKey)){
window.open(fullPathTo, '_blank')
return
}
navigateTo(fullPathTo)
}
この関数を、composablesに書くことでどこでも使い回せるようになります。
終わりに
2024年も、残るところ3週間。大学に入ったのは、今年の4月だったはずなのに、なぜか遠い過去のように感じますね。インターンも一旦来年の3月末まで延長になったので、2025年もスタートダッシュで頑張っていきたいと思います!それでは!
Discussion