⁉️

半年間の長期インターンを始めたので、1ヶ月ごとに学んだこととかを吐き出してみるの会【11月編】

2024/12/09に公開

はじめに

今月は、月末に実装したスクロール関係の実装についてと、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)を使って実装しました。

ロジックとしては、

  1. useRouter().resolve(to).fullPathで、開く先のリンクのフルパスを取得
  2. Ctrl or Cmdが押されているかどうかを判定
  3. window.open()を使って、Ctrl or Cmdが押されている場合は_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