کنترل های فرم توانمندتر

با یک رویداد جدید و APIهای عناصر سفارشی، شرکت در فرم‌ها بسیار آسان‌تر شده است.

Arthur Evans

بسیاری از توسعه‌دهندگان، کنترل‌های فرم سفارشی را می‌سازند، یا برای ارائه کنترل‌هایی که در مرورگر تعبیه نشده‌اند، یا برای سفارشی کردن ظاهر و احساس فراتر از آنچه که با کنترل‌های فرم داخلی امکان‌پذیر است.

با این حال، تکرار ویژگی‌های کنترل‌های فرم HTML داخلی ممکن است دشوار باشد. برخی از ویژگی‌هایی را در نظر بگیرید که یک عنصر <input> هنگام افزودن آن به فرم به‌طور خودکار دریافت می‌کند:

  • ورودی به طور خودکار به لیست کنترل های فرم اضافه می شود.
  • مقدار ورودی به طور خودکار همراه با فرم ارسال می شود.
  • ورودی در اعتبار سنجی فرم شرکت می کند. می‌توانید با استفاده از کلاس‌های شبه :valid و :invalid به ورودی استایل دهید.
  • هنگامی که فرم بازنشانی می‌شود، زمانی که فرم مجدداً بارگیری می‌شود، یا زمانی که مرورگر سعی می‌کند ورودی‌های فرم را به‌طور خودکار تکمیل کند، ورودی مطلع می‌شود.

کنترل‌های فرم سفارشی معمولاً تعداد کمی از این ویژگی‌ها را دارند. توسعه‌دهندگان می‌توانند برخی از محدودیت‌های جاوا اسکریپت را برطرف کنند، مانند افزودن یک <input> مخفی به فرم برای شرکت در ارسال فرم. اما سایر ویژگی ها را نمی توان به تنهایی در جاوا اسکریپت تکرار کرد.

دو ویژگی جدید وب ساخت کنترل‌های فرم سفارشی را آسان‌تر می‌کند و محدودیت‌های کنترل‌های سفارشی فعلی را حذف می‌کند:

  • رویداد formdata به یک شی جاوا اسکریپت دلخواه اجازه می دهد در ارسال فرم شرکت کند، بنابراین می توانید داده های فرم را بدون استفاده از <input> پنهان اضافه کنید.
  • API عناصر سفارشی مرتبط با فرم به عناصر سفارشی اجازه می‌دهد بیشتر شبیه کنترل‌های فرم داخلی عمل کنند.

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

API مبتنی بر رویداد

رویداد formdata یک API سطح پایین است که به هر کد جاوا اسکریپت اجازه می دهد در ارسال فرم شرکت کند. مکانیزم به این صورت عمل می کند:

  1. شما یک شنونده رویداد formdata را به فرمی که می خواهید با آن تعامل داشته باشید اضافه می کنید.
  2. هنگامی که کاربر روی دکمه ارسال کلیک می کند، فرم یک رویداد formdata را اجرا می کند که شامل یک شی FormData است که تمام داده های ارسال شده را در خود نگه می دارد.
  3. هر شنونده formdata فرصتی برای اضافه کردن یا اصلاح داده ها قبل از ارسال فرم دارد.

در اینجا مثالی از ارسال یک مقدار واحد در شنونده رویداد formdata آورده شده است:

const form = document.querySelector('form');
// FormData event is sent on <form> submission, before transmission.
// The event has a formData property
form.addEventListener('formdata', ({formData}) => {
  // https://developer.mozilla.org/docs/Web/API/FormData
  formData.append('my-input', myInputValue);
});

این را با استفاده از مثال ما در Glitch امتحان کنید. حتماً آن را در Chrome 77 یا جدیدتر اجرا کنید تا API را در عمل ببینید.

سازگاری با مرورگر

پشتیبانی مرورگر

  • 5
  • 12
  • 4
  • 5

منبع

عناصر سفارشی مرتبط با فرم

شما می‌توانید از API مبتنی بر رویداد با هر نوع مؤلفه‌ای استفاده کنید، اما فقط به شما امکان می‌دهد با فرآیند ارسال مقاله تعامل داشته باشید.

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

  • هنگامی که یک عنصر سفارشی مرتبط با فرم را در داخل یک <form> قرار می دهید، به طور خودکار با فرم مرتبط می شود، مانند یک کنترل ارائه شده توسط مرورگر.
  • عنصر را می توان با استفاده از عنصر <label> برچسب گذاری کرد.
  • عنصر می تواند مقداری را تنظیم کند که به طور خودکار همراه با فرم ارسال شود.
  • این عنصر می تواند یک پرچم تعیین کند که نشان دهد ورودی معتبر دارد یا خیر. اگر یکی از کنترل‌های فرم دارای ورودی نامعتبر باشد، فرم نمی‌تواند ارسال شود.
  • این عنصر می‌تواند برای بخش‌های مختلف چرخه عمر فرم، تماس‌های برگشتی ارائه کند - مانند زمانی که فرم غیرفعال می‌شود یا به حالت پیش‌فرض خود بازنشانی می‌شود.
  • این عنصر از شبه کلاس‌های استاندارد CSS برای کنترل‌های فرم مانند :disabled و :invalid پشتیبانی می‌کند.

که بسیاری از ویژگی ها! این مقاله به همه آنها نمی پردازد، اما اصول اولیه مورد نیاز برای ادغام عنصر سفارشی شما با یک فرم را شرح می دهد.

تعریف یک عنصر سفارشی مرتبط با فرم

برای تبدیل یک عنصر سفارشی به یک عنصر سفارشی مرتبط با فرم نیاز به چند مرحله اضافی است:

  • یک ویژگی static formAssociated را به کلاس عنصر سفارشی خود اضافه کنید. این به مرورگر می‌گوید که با عنصر مانند یک کنترل فرم رفتار کند.
  • متد attachInternals() روی عنصر را فراخوانی کنید تا به متدها و ویژگی‌های اضافی برای کنترل‌های فرم مانند setFormValue() و setValidity() دسترسی پیدا کنید.
  • ویژگی‌ها و روش‌های رایج پشتیبانی شده توسط کنترل‌های فرم، مانند name ، value و validity اضافه کنید.

در اینجا نحوه قرار گرفتن آن موارد در یک تعریف عنصر سفارشی اولیه آمده است:

// Form-associated custom elements must be autonomous custom elements--
// meaning they must extend HTMLElement, not one of its subclasses.
class MyCounter extends HTMLElement {

  // Identify the element as a form-associated custom element
  static formAssociated = true;

  constructor() {
    super();
    // Get access to the internal form control APIs
    this.internals_ = this.attachInternals();
    // internal value for this control
    this.value_ = 0;
  }

  // Form controls usually expose a "value" property
  get value() { return this.value_; }
  set value(v) { this.value_ = v; }

  // The following properties and methods aren't strictly required,
  // but browser-level form controls provide them. Providing them helps
  // ensure consistency with browser-provided controls.
  get form() { return this.internals_.form; }
  get name() { return this.getAttribute('name'); }
  get type() { return this.localName; }
  get validity() {return this.internals_.validity; }
  get validationMessage() {return this.internals_.validationMessage; }
  get willValidate() {return this.internals_.willValidate; }

  checkValidity() { return this.internals_.checkValidity(); }
  reportValidity() {return this.internals_.reportValidity(); }

  …
}
customElements.define('my-counter', MyCounter);

پس از ثبت نام، می توانید از این عنصر در هر کجا که از کنترل فرم ارائه شده توسط مرورگر استفاده می کنید استفاده کنید:

<form>
  <label>Number of bunnies: <my-counter></my-counter></label>
  <button type="submit">Submit</button>
</form>

تنظیم یک مقدار

متد attachInternals() یک شی ElementInternals را برمی گرداند که دسترسی به API های کنترل فرم را فراهم می کند. اساسی ترین آنها متد setFormValue() است که مقدار فعلی کنترل را تعیین می کند.

متد setFormValue() می تواند یکی از سه نوع مقدار را بگیرد:

  • یک مقدار رشته
  • یک شی File .
  • یک شی FormData . می‌توانید از یک شی FormData برای ارسال مقادیر متعدد استفاده کنید (به عنوان مثال، یک کنترل ورودی کارت اعتباری ممکن است شماره کارت، تاریخ انقضا و کد تأیید را ارسال کند).

برای تنظیم یک مقدار ساده:

this.internals_.setFormValue(this.value_);

برای تنظیم چندین مقدار، می توانید کاری شبیه به این انجام دهید:

// Use the control's name as the base name for submitted data
const n = this.getAttribute('name');
const entries = new FormData();
entries.append(n + '-first-name', this.firstName_);
entries.append(n + '-last-name', this.lastName_);
this.internals_.setFormValue(entries);

اعتبار سنجی ورودی

کنترل شما همچنین می‌تواند با فراخوانی متد setValidity() در شیء داخلی در اعتبارسنجی فرم شرکت کند.

// Assume this is called whenever the internal value is updated
onUpdateValue() {
  if (!this.matches(':disabled') && this.hasAttribute('required') &&
      this.value_ < 0) {
    this.internals_.setValidity({customError: true}, 'Value cannot be negative.');
  }
  else {
    this.internals_.setValidity({});
  }
  this.internals.setFormValue(this.value_);
}

می‌توانید یک عنصر سفارشی مرتبط با فرم را با شبه کلاس‌های :valid و :invalid استایل دهید، درست مانند یک کنترل فرم داخلی.

تماس های چرخه حیات

یک API عنصر سفارشی مرتبط با فرم شامل مجموعه‌ای از تماس‌های چرخه حیات اضافی برای اتصال به چرخه حیات فرم است. تماس‌های برگشتی اختیاری هستند: فقط در صورتی که عنصر شما نیاز به انجام کاری در آن نقطه از چرخه حیات داشته باشد، یک callback را اجرا کنید.

void formAssociatedCallback(form)

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

void formDisabledCallback(disabled)

پس از تغییر وضعیت disabled عنصر فراخوانی می شود، یا به این دلیل که ویژگی disabled این عنصر اضافه یا حذف شده است. یا به این دلیل که حالت disabled در یک <fieldset> که اجداد این عنصر است تغییر کرده است. پارامتر disabled نشان دهنده وضعیت غیرفعال جدید عنصر است. به عنوان مثال، عنصر ممکن است وقتی غیرفعال است، عناصر موجود در DOM سایه خود را غیرفعال کند.

void formResetCallback()

پس از تنظیم مجدد فرم تماس گرفته می شود. عنصر باید خود را به نوعی حالت پیش فرض بازنشانی کند. برای عناصر <input> ، این معمولاً شامل تنظیم ویژگی value برای مطابقت با ویژگی value تنظیم شده در نشانه‌گذاری (یا در مورد چک باکس، تنظیم ویژگی checked برای مطابقت با ویژگی checked است.

void formStateRestoreCallback(state, mode)

در یکی از دو حالت تماس گرفته می شود:

  • هنگامی که مرورگر وضعیت عنصر را بازیابی می کند (به عنوان مثال، پس از یک پیمایش، یا زمانی که مرورگر مجددا راه اندازی می شود). آرگومان mode در این مورد "restore" است.
  • وقتی ویژگی‌های کمک ورودی مرورگر مانند تکمیل خودکار فرم مقداری را تعیین می‌کند. آرگومان mode در این مورد "autocomplete" است.

نوع آرگومان اول بستگی به نحوه فراخوانی متد setFormValue() دارد. برای جزئیات بیشتر، به بازیابی حالت فرم مراجعه کنید.

بازیابی حالت فرم

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

برای یک عنصر سفارشی مرتبط با فرم، حالت بازیابی شده از مقدار(هایی) که به متد setFormValue() ارسال می کنید، می آید. می توانید روش را با یک پارامتر مقدار واحد فراخوانی کنید، همانطور که در مثال های قبلی نشان داده شده است، یا با دو پارامتر:

this.internals_.setFormValue(value, state);

value مقدار قابل ارسال کنترل را نشان می دهد. پارامتر state اختیاری یک نمایش داخلی از وضعیت کنترل است که می تواند شامل داده هایی باشد که به سرور ارسال نمی شوند. پارامتر state همان نوع پارامتر value را می گیرد - می تواند یک رشته، File یا شی FormData باشد.

پارامتر state زمانی مفید است که نمی‌توانید وضعیت کنترل را بر اساس مقدار به تنهایی بازیابی کنید. برای مثال، فرض کنید یک انتخابگر رنگ با چند حالت ایجاد می کنید: یک پالت یا یک چرخه رنگ RGB. مقدار ارسالی، رنگ انتخاب شده در یک فرم متعارف، مانند "#7fff00" خواهد بود. اما برای بازگرداندن کنترل به یک وضعیت خاص، همچنین باید بدانید که در کدام حالت قرار دارد، بنابراین وضعیت ممکن است شبیه "palette/#7fff00" باشد.

this.internals_.setFormValue(this.value_,
    this.mode_ + '/' + this.value_);

کد شما باید وضعیت خود را بر اساس مقدار حالت ذخیره شده بازیابی کند.

formStateRestoreCallback(state, mode) {
  if (mode == 'restore') {
    // expects a state parameter in the form 'controlMode/value'
    [controlMode, value] = state.split('/');
    this.mode_ = controlMode;
    this.value_ = value;
  }
  // Chrome currently doesn't handle autofill for form-associated
  // custom elements. In the autofill case, you might need to handle
  // a raw value.
}

در مورد یک کنترل ساده تر (مثلاً یک عدد ورودی)، مقدار احتمالاً برای بازگرداندن کنترل به حالت قبلی کافی است. اگر هنگام فراخوانی setFormValue() state حذف کنید، آنگاه مقدار به formStateRestoreCallback() ارسال می شود.

formStateRestoreCallback(state, mode) {
  // Simple case, restore the saved value
  this.value_ = state;
}

یک نمونه کار

مثال زیر بسیاری از ویژگی های عناصر سفارشی مرتبط با فرم را در کنار هم قرار می دهد. حتماً آن را در Chrome 77 یا جدیدتر اجرا کنید تا API را در عمل ببینید.

تشخیص ویژگی

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

if ('FormDataEvent' in window) {
  // formdata event is supported
}

if ('ElementInternals' in window &&
    'setFormValue' in window.ElementInternals.prototype) {
  // Form-associated custom elements are supported
}

نتیجه

رویداد formdata و عناصر سفارشی مرتبط با فرم ابزارهای جدیدی را برای ایجاد کنترل‌های فرم سفارشی فراهم می‌کنند.

رویداد formdata هیچ قابلیت جدیدی به شما نمی دهد، اما یک رابط برای افزودن داده های فرم خود به فرآیند ارسال، بدون نیاز به ایجاد عنصر <input> پنهان در اختیار شما قرار می دهد.

API عناصر سفارشی مرتبط با فرم مجموعه جدیدی از قابلیت‌ها را برای ایجاد کنترل‌های فرم سفارشی ارائه می‌کند که مانند کنترل‌های فرم داخلی کار می‌کنند.

تصویر قهرمان توسط Oudom Pravat در Unsplash .