پاس‌دادن # و کنترل عملیات اسکرول

Answering # and controlling Scroll Operations

Vue.JS 2: پاس دادن # و کنترل عملیات اسکرول - قسمت 84

در قسمت قبل با اضافه کردن انیمیشن به تغییر کامپوننت ها آشنا شدیم و همچنین مقدمه ای از مشکل این جلسه را بیان کردم. اگر یادتان باشد در فایل UserEdit.vue کدها را بدین شکل ویرایش کرده بودیم:

<template>
  <div>
    <h3>Edit the User</h3>
    <p>Locale: {{ $route.query.locale }}</p>
    <p>Analytics: {{ $route.query.q }}</p>
    <div style="height: 700px"></div>
    <p id="data">Some extra Data</p>
  </div>
</template>

در همان جلسه توضیح دادم که رفتار پیش فرض مرورگرها این است که با وارد کردن # در URL می توانیم id یک عنصر خاص را هدف بگیریم تا صفحه به صورت خودکار به آن عنصر برود. در کد بالا یک Div ساده را داریم که کارش اضافه کردن 700 پیکسل به طول صفحه است تا صفحه ی ما اسکرول بخورد. سپس یک تگ p ساده را داریم که برایش id تعریف کرده ایم. حالا اگر به آدرس زیر در مرورگر برویم، مستقیما خود را در قسمت این تگ پیدا خواهید کرد:

http://localhost:8080/user/1/edit?locale=en&q=100#data

سوال من این است که چطور می توانیم این # را به همراه داده اش (data) دریافت کرده و با آن کار خاصی انجام بدهیم. ما برای پاس دادن این هش (علامت هشتگ) ابتدا به فایل UserDetail.vue می رویم و این هش را به صورت یک پارامتر دیگر پاس می دهیم:

<template>
  <div>
    <h3>Some User Details</h3>
    <p>User loaded has ID: {{ $route.params.id }}</p>
    <router-link
      tag="button"
      :to="{ name: 'userEdit', params: { id: $route.params.id }, query: { locale: 'en', q: 100 }, hash: '#data' }"
      class="btn btn-primary"
    >Edit User</router-link>
  </div>
</template>

همانطور که می بینید پس از query یک خصوصیت دیگر به نام hash اضافه کرده ام و مقدار آن را برابر data# قرار داده ام. از آنجایی که مقدار پاس داده شده به to بسیار طولانی شده است و خواندن یا ویرایش آن سخت است، بهتر است مقدار آن را به صورت یک خصوصیت جداگانه در data تعریف کنیم و سپس آن را به to پاس بدهیم. این فایل هنوز هیچ تگ <Script> ای ندارد بنابراین خودتان آن را اضافه کنید:

<template>
  <div>
    <h3>Some User Details</h3>
    <p>User loaded has ID: {{ $route.params.id }}</p>
    <router-link tag="button" :to="link" class="btn btn-primary">Edit User</router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      link: {
        name: "userEdit",
        params: { id: this.$route.params.id },
        query: { locale: "en", q: 100 },
        hash: "#data"
      }
    };
  }
};
</script>

واضح است که با انجام این کار، خواندن اطلاعات to بسیار راحت تر شده است. من تغییری در کدهای قبلی نداده ام به جز اینکه برای params به جای route.params$ گفته ام this.$route.params چرا که حالا درون شیء Vue هستیم بنابراین باید از این کلید واژه استفاده نماییم. آیا با این کار مشکل ما حل شده است؟ خیر!

مشکل اینجاست که برنامه ی ما یک برنامه ی SPA است بنابراین در جا به جایی بین صفحات یا کامپوننت ها، مرورگر refresh نمی شود. به همین دلیل زمانی که با کلیک روی دکمه ی Edit User در مرورگر به صفحه ی ویرایش (آدرس http://localhost:8080/user/1/edit?locale=en&q=100#data) می رویم، به صورت خودکار به تگ <p> منتقل نشده و دیگر قابلیت اسکرول خودکار را از دست می دهیم. اسکرول شدن خودکار در مرورگر فقط زمانی اتفاق می افتد که صفحه refresh شود یا اینکه id عنصر به صورت یک لینک در صفحه وجود داشته باشد و ما روی آن کلیک کنیم. در چنین حالتی که id به صورت یک پارامتر به URL پاس داده می شود دیگر شاهد اسکرول خودکار نخواهیم بود.

اسکرول خودکار به عناصر در صفحه بر اساس id

برای اینکه به Vue بگوییم رفتار پیش فرض مرورگر مورد پسند ما است باید به فایل main.js و به قسمتی برویم که router را در آنجا تعریف کرده بودیم:

Vue.use(VueRouter);

const router = new VueRouter({
  routes,
  mode: 'history'
});

اینجا باید یک خصوصیت دیگر را به Vue پاس بدهیم که ScrollBehavior نام دارد. این خصوصیت در اصل یک متد است که رفتار اسکرول مرورگر را پیکربندی می کند و سه آرگومان را دریافت می کند:

  • to یعنی به کدام صفحه خواهیم رفت.
  • from یعنی از کدام صفحه می آییم.
  • savedPosition که موقعیت کاربر را ذخیره می کند. مثلا اگر به قسمتی از صفحه اسکرول کرده باشیم (مثلا وسط صفحه) و سپس از دکمه ی back برای برگشتن به صفحه ی قبلی استفاده کنیم، مرورگر این موقعیت را به حافظه می سپارد تا اگر خواستیم از آن استفاده کنیم.
const router = new VueRouter({
  routes,
  mode: 'history',
  scrollBehavior(to, from, savedPosition) {
    
  }
});

این متد از ما می خواهد که در نهایت یا یک سلکتور خاص را برگردانیم یا یک شیء را به همراه مختصات x و y برگردانیم، تا بداند به کجای صفحه اسکرول کند. مثلا برای برگرداندن مختصات x و y می گوییم:

const router = new VueRouter({
  routes,
  mode: 'history',
  scrollBehavior(to, from, savedPosition) {
    return { x: 0, y: 700 }
  }
});

حالا اگر به مرورگر رفته و روی دکمه ی Edit User کلیک کنید به صورت خودکار به پایین صفحه منتقل می شوید چرا که y (موقعیت عمودی - محور y ها) را روی 700 پیکسل گذاشته ایم. یادتان باشد که این رفتار به خاطر همین تابع است نه به خاطر وجود data# در URL. همچنین اگر صفحات دیگر برنامه ی ما طولانی تر بودند، در تک تک صفحات به 700 پیکسل اسکرول می شدیم. چرا؟ به دلیل اینکه هیچ فیلتری را در کد بالا تعریف نکرده ایم، بلکه گفته ایم همیشه به 700 پیکسل اسکرول کن! منتهی از آنجایی که صفحات ما طول 700 پیکسلی ندارند، چیزی را نمی بینید.

همچنین اگر بخواهید از روش پاس دادن سلکتور پیش بروید، باید به شکل زیر عمل کنید:

const router = new VueRouter({
  routes,
  mode: 'history',
  scrollBehavior(to, from, savedPosition) {
    if (to.hash) {
      return { selector: to.hash };
    }
    return { x: 0, y: 700 }
  }
});

من در این کد گفته ام که اگر صفحه ای که می خواهیم به آن برویم (در حال منتقل شدن به آن هستیم) دارای خصوصیتی به نام hash بود، به مکان همان عنصر اسکرول کن. شیء ای که در این حالت پاس داده ام دارای خصوصیت selector است که نام دلخواه نیست و یک کلیدواژه است. من مقدار این selector را برابر To.hash گذاشته ام که یعنی همان عنصری که دارای id برابر hash است. در غیر این صورت به طور پیش فرض به 700 پیکسل اسکرول می شویم. حتی اگر 700 پیکسل را کامنت کنید باز هم در صفحه ی edit user به قسمت id=data اسکرول خواهیم شد.

در نهایت باید با savedPosition کار کنیم. فرض کنید کاربری روی editUser کلیک کرده است و به پایین صفحه رفته، سپس روی لینک دیگری کلیک کرده و به صفحات دیگری رفته است. حالا اگر کلید back را بزند و به صفحه ی قبلی (صفحه ای که # دارد) برود، خوب نیست که او را به بالای صفحه اسکرول کنیم (البته این موضوع سلیقه ای است) بلکه بهتر است که در همان موقعیت قبلی خودش باشد. بنابراین می توان گفت:

const router = new VueRouter({
  routes,
  mode: 'history',
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition;
    }
    if (to.hash) {
      return { selector: to.hash };
    }
    return { x: 0, y: 0 }
  }
});

یعنی اگر از دکمه ی back استفاده شده بود (اگر SavedPosition داشتیم) کاربر را به همان مکان قبلی اش در صفحه برگردان و اگر در URL از # استفاده شده بود، کاربر را به موقعیت آن عنصر با آیدی # ببر و اگر هیچ کدام صحیح نبود، کاربر را با ابتدای صفحه (موقعیت های 0 و 0 برای طول و عرض) ببر.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش رایگان Vue js از صفر تا صد توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.