بهترین شیوه های عنصر سفارشی

عناصر سفارشی به شما امکان می دهند تگ های HTML خود را بسازید. این چک لیست بهترین روش ها را پوشش می دهد تا به شما در ساخت عناصر با کیفیت بالا کمک کند.

عناصر سفارشی به شما امکان می دهند HTML را گسترش دهید و تگ های خود را تعریف کنید. آنها یک ویژگی فوق العاده قدرتمند هستند، اما سطح پایینی نیز دارند، به این معنی که همیشه روشن نیست که چگونه عنصر خود را به بهترین نحو پیاده سازی کنید.

برای کمک به شما در ایجاد بهترین تجربیات ممکن، این چک لیست را گردآوری کرده ایم. همه چیزهایی را که ما فکر می کنیم برای داشتن یک عنصر سفارشی با رفتار خوب لازم است را تجزیه می کند.

چک لیست

سایه DOM

یک ریشه سایه برای کپسوله کردن سبک ها ایجاد کنید.

چرا؟ کپسوله کردن سبک ها در ریشه سایه عنصر شما تضمین می کند که بدون توجه به جایی که از آن استفاده می شود، کار می کند. این امر به ویژه در صورتی مهم است که یک توسعه دهنده بخواهد عنصر شما را در ریشه سایه عنصر دیگری قرار دهد. این حتی در مورد عناصر ساده مانند یک چک باکس یا دکمه رادیویی نیز صدق می کند. ممکن است تنها محتوای موجود در ریشه سایه شما، خود استایل ها باشد.
مثال عنصر <howto-checkbox> .

ریشه سایه خود را در سازنده ایجاد کنید.

چرا؟ سازنده زمانی است که شما دانش انحصاری از عنصر خود دارید. زمان بسیار خوبی برای تنظیم جزئیات پیاده‌سازی است که نمی‌خواهید عناصر دیگر با آن‌ها درگیر شوند. انجام این کار در تماس‌های بعدی، مانند connectedCallback ، به این معنی است که باید از موقعیت‌هایی که عنصر شما جدا شده و سپس دوباره به سند متصل می‌شود، محافظت کنید.
مثال عنصر <howto-checkbox> .

هر فرزندی را که عنصر ایجاد می کند در ریشه سایه خود قرار دهید.

چرا؟ فرزندان ایجاد شده توسط عنصر شما بخشی از اجرای آن هستند و باید خصوصی باشند. بدون محافظت از یک ریشه سایه، خارج از جاوا اسکریپت ممکن است به طور ناخواسته با این کودکان تداخل ایجاد کند.
مثال عنصر <howto-tabs> .

از <slot> برای نمایش کودکان DOM روشن به DOM سایه خود استفاده کنید

چرا؟ به کاربران مؤلفه خود اجازه دهید محتوا را در مؤلفه شما مشخص کنند زیرا فرزندان HTML مؤلفه شما را قابل ترکیب تر می کند. هنگامی که یک مرورگر از عناصر سفارشی پشتیبانی نمی کند، محتوای تودرتو در دسترس، قابل مشاهده و در دسترس باقی می ماند.
مثال عنصر <howto-tabs> .

یک سبک نمایش :host را تنظیم کنید (مثلاً block ، inline-block ، flex ) مگر اینکه پیش‌فرض inline را ترجیح دهید.

چرا؟ عناصر سفارشی به صورت پیش‌فرض display: inline می‌شوند، بنابراین تنظیم width یا height آن‌ها تاثیری نخواهد داشت. این اغلب برای توسعه دهندگان غافلگیرکننده است و ممکن است باعث ایجاد مشکلاتی در زمینه چیدمان صفحه شود. اگر صفحه نمایش inline را ترجیح نمی دهید، همیشه باید مقدار display پیش فرض را تنظیم کنید.
مثال عنصر <howto-checkbox> .

یک سبک نمایش :host اضافه کنید که به ویژگی پنهان احترام می گذارد.

چرا؟ یک عنصر سفارشی با سبک display پیش‌فرض، به‌عنوان مثال :host { display: block } ، ویژگی hidden داخلی با ویژگی پایین‌تر را لغو می‌کند. این ممکن است شما را شگفت زده کند اگر انتظار داشته باشید که ویژگی hidden را روی عنصر خود تنظیم کنید تا display: none . علاوه بر یک سبک display پیش‌فرض، پشتیبانی از hidden را با :host([hidden]) { display: none } اضافه کنید.
مثال عنصر <howto-checkbox> .

صفات و خواص

ویژگی‌های کلی مجموعه نویسنده را نادیده نگیرید.

چرا؟ ویژگی های جهانی آنهایی هستند که در تمام عناصر HTML وجود دارند. برخی از نمونه‌ها عبارتند از tabindex و role . یک عنصر سفارشی ممکن است بخواهد tabindex اولیه خود را روی 0 تنظیم کند تا قابل فوکوس روی صفحه کلید باشد. اما همیشه باید ابتدا بررسی کنید که آیا توسعه دهنده ای که از عنصر شما استفاده می کند این مقدار را روی مقدار دیگری تنظیم کرده است یا خیر. برای مثال، اگر tabindex روی -1 تنظیم کرده باشند، این علامتی است که نمی‌خواهند عنصر تعاملی باشد.
مثال عنصر <howto-checkbox> . این در قسمت نویسنده صفحه را نادیده نگیرید بیشتر توضیح داده شده است.

همیشه داده های اولیه (رشته ها، اعداد، بولی ها) را به عنوان ویژگی یا ویژگی بپذیرید.

چرا؟ عناصر سفارشی، مانند همتایان داخلی خود، باید قابل تنظیم باشند. پیکربندی را می توان به صورت اعلامی، از طریق ویژگی ها، یا به طور ضروری از طریق خصوصیات جاوا اسکریپت منتقل کرد. در حالت ایده‌آل، هر ویژگی باید به یک ویژگی مربوطه پیوند داده شود.
مثال عنصر <howto-checkbox> .

هدف این است که ویژگی‌ها و ویژگی‌های داده اولیه را همگام نگه دارید، از یک ویژگی به ویژگی دیگر منعکس کنید، و بالعکس.

چرا؟ شما هرگز نمی دانید که یک کاربر چگونه با عنصر شما تعامل خواهد داشت. آنها ممکن است یک ویژگی را در جاوا اسکریپت تنظیم کنند و سپس انتظار داشته باشند که آن مقدار را با استفاده از یک API مانند getAttribute() بخوانند. اگر هر خصیصه خاصیت متناظری داشته باشد و هر دوی آنها منعکس شوند، کار با عنصر شما را برای کاربران آسان تر می کند. به عبارت دیگر، فراخوانی setAttribute('foo', value) نیز باید یک ویژگی foo مربوطه را تنظیم کند و بالعکس. البته استثناهایی هم در این قاعده وجود دارد. شما نباید ویژگی های فرکانس بالا را منعکس کنید، به عنوان مثال currentTime در یک پخش کننده ویدیو. از بهترین قضاوت خود استفاده کنید. اگر به نظر می رسد که کاربر با یک ویژگی یا ویژگی تعامل دارد و بازتاب آن دشوار نیست، این کار را انجام دهید.
مثال عنصر <howto-checkbox> . این در قسمت اجتناب از مسائل مربوط به ورود مجدد توضیح داده شده است.

هدف این است که فقط داده های غنی (اشیاء، آرایه ها) را به عنوان ویژگی بپذیرید.

چرا؟ به طور کلی، هیچ نمونه ای از عناصر داخلی HTML وجود ندارد که داده های غنی (اشیاء و آرایه های جاوا اسکریپت ساده) را از طریق ویژگی های خود بپذیرد. داده‌های غنی از طریق فراخوانی متد یا ویژگی‌ها پذیرفته می‌شوند. چند جنبه منفی آشکار برای پذیرش داده های غنی به عنوان ویژگی وجود دارد: سریال کردن یک شی بزرگ به یک رشته می تواند گران باشد و هر مرجع شی در این فرآیند رشته بندی از بین خواهد رفت. به عنوان مثال، اگر یک شی را که دارای ارجاع به یک شی دیگر، یا شاید یک گره DOM است، رشته کنید، آن ارجاعات از بین خواهند رفت.

ویژگی های داده های غنی را به ویژگی ها منعکس نکنید.

چرا؟ انعکاس ویژگی های داده غنی به ویژگی ها بیهوده پرهزینه است و به سریال سازی و سریال زدایی از همان اشیاء جاوا اسکریپت نیاز دارد. اگر مورد استفاده ای ندارید که فقط با این ویژگی قابل حل است، احتمالاً بهتر است از آن اجتناب کنید.

بررسی ویژگی هایی که ممکن است قبل از ارتقاء عنصر تنظیم شده باشند را در نظر بگیرید.

چرا؟ توسعه‌دهنده‌ای که از عنصر شما استفاده می‌کند ممکن است سعی کند قبل از بارگیری تعریف عنصر، ویژگی را روی آن تنظیم کند. این امر مخصوصاً زمانی صادق است که توسعه‌دهنده از چارچوبی استفاده می‌کند که بارگذاری مؤلفه‌ها را کنترل می‌کند، آنها را به صفحه مهر می‌کند و ویژگی‌های آنها را به یک مدل متصل می‌کند.
مثال عنصر <howto-checkbox> . توضیح بیشتر در ساخت خواص تنبل .

از کلاس های خود استفاده نکنید.

چرا؟ عناصری که باید حالت خود را بیان کنند باید با استفاده از ویژگی ها این کار را انجام دهند. مشخصه class معمولاً متعلق به توسعه دهنده با استفاده از عنصر شما است و نوشتن در آن ممکن است به طور ناخواسته به کلاس های توسعه دهنده ضربه بزند.

رویدادها

رویدادها را در پاسخ به فعالیت اجزای داخلی ارسال کنید.

چرا؟ مؤلفه شما ممکن است دارای ویژگی هایی باشد که در پاسخ به فعالیتی که فقط مؤلفه شما از آن اطلاع دارد، تغییر می کند، برای مثال، اگر یک تایمر یا انیمیشن کامل شود یا یک منبع بارگیری را به پایان برساند. ارسال رویدادها در پاسخ به این تغییرات برای اطلاع میزبان از متفاوت بودن وضعیت مؤلفه مفید است.

رویدادها را در پاسخ به تنظیمات میزبان یک ویژگی (جریان داده رو به پایین) ارسال نکنید.

چرا؟ ارسال یک رویداد در پاسخ به یک میزبان که یک ویژگی را تنظیم می کند، اضافی است (میزبان وضعیت فعلی را می داند زیرا فقط آن را تنظیم کرده است). ارسال رویدادها در پاسخ به یک میزبان که یک ویژگی را تنظیم می کند ممکن است باعث ایجاد حلقه های بی نهایت با سیستم های اتصال داده شود.
مثال عنصر <howto-checkbox> .

توضیح دهندگان

نویسنده صفحه را نادیده نگیرید

ممکن است توسعه‌دهنده‌ای که از عنصر شما استفاده می‌کند بخواهد برخی از حالت اولیه آن را لغو کند. به عنوان مثال، تغییر role ARIA یا قابلیت تمرکز آن با tabindex . قبل از اعمال مقادیر خود، بررسی کنید که آیا این و سایر ویژگی‌های جهانی تنظیم شده‌اند.

connectedCallback() {
  if (!this.hasAttribute('role'))
    this.setAttribute('role', 'checkbox');
  if (!this.hasAttribute('tabindex'))
    this.setAttribute('tabindex', 0);

خواص را تنبل کنید

یک توسعه‌دهنده ممکن است سعی کند یک ویژگی را قبل از بارگیری عنصر شما روی آن تنظیم کند. این امر به ویژه زمانی صادق است که توسعه‌دهنده از چارچوبی استفاده می‌کند که بارگذاری مؤلفه‌ها را مدیریت می‌کند، آنها را در صفحه وارد می‌کند و ویژگی‌های آنها را به یک مدل متصل می‌کند.

در مثال زیر، Angular به طور اعلانی ویژگی isChecked مدل خود را به ویژگی checkbox در checked باکس متصل می کند. اگر تعریف جعبه چک کردن تنبل بارگذاری شده باشد، ممکن است Angular سعی کند قبل از ارتقاء عنصر، ویژگی علامت‌گذاری شده را تنظیم کند.

<howto-checkbox [checked]="defaults.isChecked"></howto-checkbox>

یک عنصر سفارشی باید این سناریو را با بررسی اینکه آیا ویژگی‌هایی قبلاً روی نمونه آن تنظیم شده است یا خیر، مدیریت کند. <howto-checkbox> این الگو را با استفاده از روشی به نام _upgradeProperty() نشان می دهد.

connectedCallback() {
  ...
  this._upgradeProperty('checked');
}

_upgradeProperty(prop) {
  if (this.hasOwnProperty(prop)) {
    let value = this[prop];
    delete this[prop];
    this[prop] = value;
  }
}

_upgradeProperty() مقدار را از نمونه ارتقا نیافته دریافت می کند و ویژگی را حذف می کند تا تنظیم کننده ویژگی خود عنصر سفارشی را تحت الشعاع قرار ندهد. به این ترتیب، هنگامی که تعریف عنصر در نهایت بارگذاری می شود، می تواند بلافاصله وضعیت صحیح را منعکس کند.

از مسائل مربوط به ورود مجدد خودداری کنید

وسوسه انگیز است که از attributeChangedCallback() برای انعکاس وضعیت به یک ویژگی اساسی استفاده کنید، به عنوان مثال:

// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback(name, oldValue, newValue) {
  if (name === 'checked')
    this.checked = newValue;
}

اما این می تواند یک حلقه بی نهایت ایجاد کند اگر تنظیم کننده ویژگی نیز به ویژگی منعکس شود.

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    // OOPS! This will cause an infinite loop because it triggers the
    // attributeChangedCallback() which then sets this property again.
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

یک جایگزین این است که به تنظیم کننده خصوصیت اجازه دهید تا به ویژگی منعکس شود و گیرنده مقدار آن را بر اساس ویژگی تعیین کند.

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

get checked() {
  return this.hasAttribute('checked');
}

در این مثال، افزودن یا حذف ویژگی، ویژگی را نیز تنظیم می کند.

در نهایت، attributeChangedCallback() را می توان برای کنترل عوارض جانبی مانند اعمال حالت های ARIA استفاده کرد.

attributeChangedCallback(name, oldValue, newValue) {
  const hasValue = newValue !== null;
  switch (name) {
    case 'checked':
      // Note the attributeChangedCallback is only handling the *side effects*
      // of setting the attribute.
      this.setAttribute('aria-checked', hasValue);
      break;
    ...
  }
}