مكوّنات طريقة التنفيذ - مربّع اختيار طريقة التنفيذ

ملخّص

تمثّل السمة <howto-checkbox> خيارًا منطقيًا في نموذج. النوع الأكثر شيوعًا من مربعات الاختيار هو النوع المزدوج الذي يسمح للمستخدم بالتبديل بين خيارين: التحديد وغير المحدد.

يحاول العنصر أن يطبّق السمتَين role="checkbox" وtabindex="0" ذاتيًا عند إنشائه لأول مرة. تساعد السمة role التكنولوجيا المساعِدة، مثل قارئ الشاشة في تحديد نوع التحكّم. تفعِّل السمة tabindex العنصر في ترتيب التنقل بـ Tab، ما يجعله قابلاً للتركيز والتشغيل من خلال لوحة المفاتيح. للمزيد من المعلومات حول هذين الموضوعَين، يمكنك الاطّلاع على القسم ما الذي يمكن أن يفعله ARIA؟ وباستخدام tabindex.

عند وضع علامة في مربّع الاختيار، تتم إضافة سمة منطقية checked وضبط سمة checked مقابلة على true. بالإضافة إلى ذلك، يضبط العنصر السمة aria-checked إما على "true" أو "false"، حسب حالتها. يؤدي النقر على مربع الاختيار باستخدام الماوس أو مفتاح المسافة إلى تبديل الحالات المحددة هذه.

يدعم مربّع الاختيار أيضًا حالة disabled. في حال ضبط السمة disabled على "صحيح" أو تطبيق السمة disabled، يتم ضبط السمة aria-disabled="true" على مربّع الاختيار وإزالة السمة tabindex وإعادة التركيز على المستند إذا كان مربّع الاختيار هو activeElement الحالي.

يتم إقران مربّع الاختيار بعنصر howto-label للتأكّد من أنّه يتضمّن اسمًا يسهل الوصول إليه.

مَراجع

تجريبي

مشاهدة العرض التوضيحي المباشر على GitHub

مثال للاستخدام

<style>
  howto-checkbox {
    vertical-align: middle;
  }
  howto-label {
    vertical-align: middle;
    display: inline-block;
    font-weight: bold;
    font-family: sans-serif;
    font-size: 20px;
    margin-left: 8px;
  }
</style>

<howto-checkbox id="join-checkbox"></howto-checkbox>
<howto-label for="join-checkbox">Join Newsletter</howto-label>

الرمز

(function() {

يمكنك تحديد رموز المفاتيح للمساعدة في التعامل مع أحداث لوحة المفاتيح.

  const KEYCODE = {
    SPACE: 32,
  };

إنّ استنساخ المحتوى من عنصر <template> أكثر فعالية من استخدام innerHTML لأنّه يتجنّب التكاليف الإضافية لتحليل HTML.

  const template = document.createElement('template');

  template.innerHTML = `
    <style>
      :host {
        display: inline-block;
        background: url('../images/unchecked-checkbox.svg') no-repeat;
        background-size: contain;
        width: 24px;
        height: 24px;
      }
      :host([hidden]) {
        display: none;
      }
      :host([checked]) {
        background: url('../images/checked-checkbox.svg') no-repeat;
        background-size: contain;
      }
      :host([disabled]) {
        background:
          url('../images/unchecked-checkbox-disabled.svg') no-repeat;
        background-size: contain;
      }
      :host([checked][disabled]) {
        background:
          url('../images/checked-checkbox-disabled.svg') no-repeat;
        background-size: contain;
      }
    </style>
  `;


  class HowToCheckbox extends HTMLElement {
    static get observedAttributes() {
      return ['checked', 'disabled'];
    }

يتم تشغيل الدالة الإنشائية للعنصر في أي وقت يتم فيه إنشاء مثيل جديد. يتم إنشاء المثيلات إما عن طريق تحليل HTML، أو استدعاء document.createElement('howto-checkbox')، أو استدعاء HowToCheckbox() جديد؛ وتُعد الدالة الإنشائية مكانًا جيدًا لإنشاء shadow DOM، على الرغم من أنّه يجب تجنّب لمس أي سمات أو عناصر فرعية لـ light DOM لأنها قد لا تكون متاحة بعد.

    constructor() {
      super();
      this.attachShadow({mode: 'open'});
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }

يتم تنشيط connectedCallback() عند إدراج العنصر في DOM. ويفضَّل ضبط الإعدادات الأولية role وtabindex والحالة الداخلية وتثبيت أدوات معالجة الأحداث.

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

قد يقوم المستخدم بتعيين خاصية على مثيل عنصر ما، قبل ربط نموذجه الأولي بهذه الفئة. ستتحقّق الطريقة _upgradeProperty() من أيّ خصائص للمثيل وتجريها من خلال أدوات ضبط الفئة المناسبة. لمزيد من التفاصيل، يمكنك الاطّلاع على قسم المواقع الإلكترونية الكسولة.

      this._upgradeProperty('checked');
      this._upgradeProperty('disabled');

      this.addEventListener('keyup', this._onKeyUp);
      this.addEventListener('click', this._onClick);
    }

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

يتم تنشيط disconnectedCallback() عند إزالة العنصر من DOM. يُعدّ هذا الخيار وسيلة جيدة للقيام بأعمال تنظيم مثل إصدار المراجع وإزالة أدوات معالجة الأحداث.

    disconnectedCallback() {
      this.removeEventListener('keyup', this._onKeyUp);
      this.removeEventListener('click', this._onClick);
    }

يجب أن تتطابق الخصائص والسمات المقابلة لها مع بعضها. تتعامل سمة تعيين السمة المحددة مع القيم الصادقة/الزائفة وتعكس هذه القيم إلى حالة السمة. راجِع قسم تجنُّب إعادة الانضمام للحصول على مزيد من التفاصيل.

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

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

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

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

يتم استدعاء attributeChangedCallback() عندما يتم تغيير أي من السمات في مصفوفة لعبة ExploreAttributes. ويُعدّ هذا الملف مكانًا جيدًا للتعامل مع الآثار الجانبية، مثل ضبط سمات ARIA.

    attributeChangedCallback(name, oldValue, newValue) {
      const hasValue = newValue !== null;
      switch (name) {
        case 'checked':
          this.setAttribute('aria-checked', hasValue);
          break;
        case 'disabled':
          this.setAttribute('aria-disabled', hasValue);

لا توفّر السمة tabindex طريقة لإزالة إمكانية التركيز بالكامل من عنصر. سيظلّ بإمكانك التركيز على العناصر التي تتضمّن tabindex=-1 باستخدام الماوس أو من خلال طلب الرقم focus(). للتأكّد من إيقاف عنصر وعدم إمكانية التركيز عليه، أزِل السمة tabindex.

          if (hasValue) {
            this.removeAttribute('tabindex');

إذا كان التركيز حاليًا على هذا العنصر، يمكنك إلغاء التركيز عليه من خلال استدعاء طريقة HTMLElement.blur().

            this.blur();
          } else {
            this.setAttribute('tabindex', '0');
          }
          break;
      }
    }

    _onKeyUp(event) {

لا تتعامل مع اختصارات التعديل التي تستخدمها عادةً التكنولوجيا المساعدة.

      if (event.altKey)
        return;

      switch (event.keyCode) {
        case KEYCODE.SPACE:
          event.preventDefault();
          this._toggleChecked();
          break;

ويتم تجاهل أي ضغطة أخرى على المفاتيح وتمريرها مرة أخرى إلى المتصفّح.

        default:
          return;
      }
    }

    _onClick(event) {
      this._toggleChecked();
    }

يستدعي _toggleChecked() أداة الضبط التي تم وضع علامة عليها ويقلب حالتها. بما أنّ _toggleChecked() لا يحدث إلا بسبب إجراء من جانب المستخدم، سيتم أيضًا إرسال حدث تغيير. يظهر هذا الحدث كفقاعات لمحاكاة سلوك <input type=checkbox> الأصلي.

    _toggleChecked() {
      if (this.disabled)
        return;
      this.checked = !this.checked;
      this.dispatchEvent(new CustomEvent('change', {
        detail: {
          checked: this.checked,
        },
        bubbles: true,
      }));
    }
  }

  customElements.define('howto-checkbox', HowToCheckbox);
})();