پروژه Drag & Drop: کار با عناصر DOM

Drag & Drop project: Working with DOM Elements

15 مرداد 1399
پروژه ی Drag & Drop: کار با عناصر DOM

در قسمت قبل توضیحی کلی از ساختار پروژه را خدمت شما ارائه کردم و حالا در این قسمت باید شروع به کدنویسی کنیم. برای شروع دستور npm start را در ترمینال اجرا می کنم تا سرور توسعه محلی ما راه بیفتد. سپس در یک پنجره ترمینال دیگر دستور tsc -w را اجرا می کنم تا وارد watch mode شویم. راه های زیادی برای نوشتن این برنامه وجود دارد اما از آنجا که ما در مورد کلاس ها و غیره صحبت کردیم من پروژه را به صورت شیء گرا پیاده سازی می کنم.

ابتدا کلاسی به نام projectInput می سازم:

class ProjectInput {

}

هدف ما در این کلاس این است که به Template و فرم درون آن دسترسی پیدا کرده و سپس آن را درون Div پایانی (در فایل index.html با آیدی app) نمایش بدهیم. در ابتدا constructor ای می سازم که درون آن به template و محل نمایش آن (div با آیدی app) دسترسی پیدا کنیم:

class ProjectInput {

    constructor() {
        this.templateElement =

        this.hostElement =
    }
}

من این نام ها را انتخاب کرده ام، اما شما می توانید هر نام دیگری را انتخاب کنید. حالا می گوییم:

class ProjectInput {

    templateElement: HTMLTemplateElement;
    hostElement: HTMLDivElement;

    constructor() {
        this.templateElement = document.getElementById(
            'project-input'
        )! as HTMLTemplateElement;

        this.hostElement =
    }
}

همانطور که می بینید این دو خصوصیت را تعریف کرده ایم و به آن ها تایپ های مورد نظرشان را داده ایم تا بعدا به خطا برنخوریم. اگر این کار را نکنیم تایپ اسکریپت به ما خطا می دهد که چرا سعی در مقداردهی به خصوصیتی داری که اصلا در کلاس ProjectInput وجود ندارد؟ در مرحله بعد و پس از اضافه کردن این خصوصیت خارج از constructor به خطای جدیدی برخواهیم خورد که می گوید ممکن است مقدار این خصوصیت null باشد. اگر یادتان باشد در فصل های قبلی به شما گفتم که در چنین حالتی باید به تایپ اسکریپت بگوییم ما به کدهای HTML دسترسی داشته و مطمئن هستیم که مقدار درون تگ Template هیچ وقت برابر null نمی شود. این کار با استفاده از علامت ! انجام خواهد شد. بنابراین به کد زیر می رسیم:

this.templateElement = document.getElementById('project-input')!;

اما هنوز هم تایپ اسکریپت به ما خطا می دهد. چرا؟ به دلیل اینکه دستور getElementById نمی تواند تشخیص دهد که دقیقا چه عنصر HTML ای را برمی گرداند، بلکه فقط می داند نوعی عنصر HTML برگردانده خواهد شد (مثلا یک دکمه، یک فیلد، یک پاراگراف و ...). تایپ اسکریپت این مورد را خطا می داند و به ما می گوید که حتما نوع دقیق آن را برای getElementById مشخص کنیم. از آنجایی که ما مطمئن هستیم مقدار این template چه چیزی خواهد بود می توانیم از type casting استفاده کنیم. اگر از فصل های قبلی به یاد داشته باشید روش اول آن به شکل زیر بود:

this.templateElement = <HTMLTemplateElement>document.getElementById('project-input')!;

یعنی قبل از دستور با علامت های <> نوع آن را مشخص کنید. روش دوم نیز استفاده از کلیدواژه as است:

this.templateElement = document.getElementById('project-input')! as HTMLTemplateElement;

از آنجایی که روش دوم تمیزتر است من از همین روش استفاده کرده ام که در کد اولیه بالا به شما نشان داده بودم. حالا نوبت به hostElement است:

class ProjectInput {
    templateElement: HTMLTemplateElement;
    hostElement: HTMLDivElement;

    constructor() {
        this.templateElement = <HTMLTemplateElement>document.getElementById('project-input')!;
        this.hostElement = document.getElementById('app')! as HTMLDivElement;
    }
}

برای مرحله بعدی باید کدی بنویسیم که محتوای درون Template را گرفته و درون DOM (تگ Div با آیدی app) نمایش بدهد. از آنجایی که می خواهم با بارگذاری برنامه حتما فرم نمایش داده شود، این کار را در constructor انجام می دهم:

class ProjectInput {
    templateElement: HTMLTemplateElement;
    hostElement: HTMLDivElement;

    constructor() {
        this.templateElement = <HTMLTemplateElement>document.getElementById('project-input')!;
        this.hostElement = document.getElementById('app')! as HTMLDivElement;

        const importedNode = document.importNode(
            this.templateElement.content,
            true
        );
    }
}

دستور importNode یک node را کپی می کند بنابراین ما محتوای (content) آن template را کپی کرده ایم و درون ثابتی به نام importedNode قرار داده ایم. آرگومان دومی که به imporNode داده ام true می باشد. اگر مانند من آرگومان دوم را true بگذارید یعنی تمام عناصر تو در تو (nested) را نیز به صورت deep کپی کند و به ما بدهد نه فقط عناصر سطحی را. در مرحله بعد باید متدی تعریف کنیم که مسئول وارد کردن عناصر ما باشد:

class ProjectInput {
  templateElement: HTMLTemplateElement;
  hostElement: HTMLDivElement;
  element: HTMLFormElement;

  constructor() {
    this.templateElement = document.getElementById(
      'project-input'
    )! as HTMLTemplateElement;
    this.hostElement = document.getElementById('app')! as HTMLDivElement;

    const importedNode = document.importNode(
      this.templateElement.content,
      true
    );
    this.element = importedNode.firstElementChild as HTMLFormElement;
    this.attach();
  }

  private attach() {
    this.hostElement.insertAdjacentElement('afterbegin', this.element);
  }
}

const prjInput = new ProjectInput();

من نام این متد را attach گذاشته ام و همانطور که می بینید مقدار 'afterbegin' را به متد insertAdjacentElement داده ام که یعنی عنصر من را در همان ابتدای تگ template (تگ آغازین آن) وارد کن. ما می خواهیم importedNode را وارد کنیم اما نمی توانیم آن را به عنوان آرگومان دوم  insertAdjacentElement و به صورت مستقیم پاس بدهیم. چرا؟ به دلیل اینکه اولا importedNode فقط از درون constructor قابل دسترسی است. دوما تایپ اسکریپت نوع این ثابت را DocumentFragment می شناسد و نمی توانیم چنین تایپی را مستقیما وارد این دستور کنیم. برای حل این مشکل یک خصوصیت دیگر به نام element تعریف کرده ایم تا مقدار اصلی HTML آن را بگیرد. دستور firstElementChild یعنی اولین فرزند درون importedNode (یعنی همان تگ های template) را بگیر تا مقدار واقعی HTML را داشته باشیم.

حالا می توانیم this.element را به insertAdjacentElement پاس بدهیم و متد attach را درون constructor صدا بزنیم. در نهایت یک نمونه از این کلاس را ساخته ایم تا کدهای آن اجرا شوند. حالا به محض ذخیره سازی کدها و رفتن به مرورگر، فرم را مشاهده می کنید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش جامع تایپ اسکریپت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

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

Kourosh
11 مهر 1402
چیزی که نفهمیدم اینه که خب چرا کلا از تگ template استفاده کردیم؟

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

مقالات مرتبط
ما را دنبال کنید
اینستاگرام روکسو تلگرام روکسو ایمیل و خبرنامه روکسو