Daha özellikli form kontrolleri

Yeni etkinlik ve özel öğeler API'leriyle formlara katılmak çok daha kolay hale geldi.

Arthur Evans

Birçok geliştirici, tarayıcıda yerleşik olmayan denetimler sağlamak veya yerleşik form denetimleriyle mümkün olanın ötesinde görünüm ve tarzı özelleştirmek için özel form denetimleri oluşturur.

Ancak yerleşik HTML form denetimlerinin özelliklerini kopyalamak zor olabilir. <input> öğesini forma eklediğinizde otomatik olarak elde edilen özelliklerden bazılarını değerlendirin:

  • Giriş, formun denetim listesine otomatik olarak eklenir.
  • Girişin değeri formla otomatik olarak gönderilir.
  • Giriş, form doğrulamasına katılır. :valid ve :invalid sözde sınıflarını kullanarak giriş stilini belirleyebilirsiniz.
  • Form sıfırlandığında, form yeniden yüklendiğinde veya tarayıcı form girişlerini otomatik olarak doldurmaya çalıştığında girişe bildirim gönderilir.

Özel form denetimleri genellikle bu özelliklerden birkaçına sahiptir. Geliştiriciler, form gönderimine katılmak için forma gizli bir <input> ekleme gibi JavaScript'in bazı sınırlamalarını geçici olarak ortadan kaldırabilirler. Ancak diğer özellikler tek başına JavaScript'te kopyalanamaz.

Özel form denetimleri oluşturmayı kolaylaştıran ve mevcut özel denetimlerin sınırlamalarını kaldıran iki yeni web özelliği vardır:

  • formdata etkinliği, rastgele bir JavaScript nesnesinin form gönderme işlemine katılmasına olanak tanır. Böylece gizli bir <input> kullanmadan form verileri ekleyebilirsiniz.
  • Form ile ilişkili özel öğeler API'si, özel öğelerin yerleşik form kontrolleri gibi çalışmasını sağlar.

Bu iki özellik, daha iyi çalışan yeni kontrol türleri oluşturmak için kullanılabilir.

Etkinliğe dayalı API

formdata etkinliği, tüm JavaScript kodlarının form gönderimine katılmasına izin veren alt düzey bir API'dir. Mekanizma şu şekilde çalışır:

  1. Etkileşimde bulunmak istediğiniz forma bir formdata etkinlik işleyicisi eklersiniz.
  2. Kullanıcı gönder düğmesini tıkladığında form, gönderilmekte olan tüm verileri tutan bir FormData nesnesini içeren bir formdata etkinliği tetikler.
  3. Her formdata dinleyicisi, form gönderilmeden önce verilere ekleme veya verilerde değişiklik yapma şansına sahip olur.

formdata etkinlik işleyicide tek bir değer göndermeyle ilgili bir örneği burada bulabilirsiniz:

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 örneğimizi kullanarak bunu deneyin. API'yi iş başında görmek için API'yi Chrome 77 veya sonraki bir sürümde çalıştırdığınızdan emin olun.

Tarayıcı uyumluluğu

Tarayıcı Desteği

  • 5
  • 12
  • 4
  • 5

Kaynak

Formla ilişkili özel öğeler

Etkinliğe dayalı API'yı herhangi bir bileşenle kullanabilirsiniz, ancak yalnızca gönderme işlemiyle etkileşimde bulunmanıza olanak tanır.

Standartlaştırılmış form denetimleri, form gönderme sürecinin yanı sıra form yaşam döngüsünün birçok bölümüne katılır. Formla ilişkili özel öğelerin amacı, özel widget'lar ve yerleşik kontroller arasındaki boşluğu doldurmaktır. Formla ilişkili özel öğeler, standartlaştırılmış form öğelerinin birçok özelliğiyle eşleşir:

  • <form> içine formla ilişkilendirilmiş bir özel öğe yerleştirdiğinizde, söz konusu öğe tarayıcı tarafından sağlanan kontrol gibi formla otomatik olarak ilişkilendirilir.
  • Bu öğe, <label> öğesi kullanılarak etiketlenebilir.
  • Öğe, formla otomatik olarak gönderilen bir değeri ayarlayabilir.
  • Öğe, geçerli bir girişe sahip olup olmadığını belirten bir işaret ayarlayabilir. Form kontrollerinden birinde geçersiz giriş varsa form gönderilemez.
  • Öğe, formun devre dışı bırakılması veya varsayılan durumuna sıfırlanması gibi form yaşam döngüsünün çeşitli bölümleri için geri çağırmalar sağlayabilir.
  • Öğe, form kontrolleri için :disabled ve :invalid gibi standart CSS sözde sınıflarını destekler.

Çok fazla özellik! Bu makalede bunların hepsine değinilmeyecek, ancak özel öğenizi formla entegre etmek için gereken temel bilgiler açıklanacaktır.

Formla ilişkili özel öğe tanımlama

Bir özel öğeyi formla ilişkili özel öğeye dönüştürmek için uygulanması gereken birkaç ek adım vardır:

  • Özel öğe sınıfınıza statik bir formAssociated özelliği ekleyin. Bu, tarayıcıya öğeyi bir form kontrolü gibi ele almasını bildirir.
  • Form kontrolleriyle ilgili setFormValue() ve setValidity() gibi ekstra yöntem ve özelliklere erişmek için öğede attachInternals() yöntemini çağırın.
  • name, value ve validity gibi form denetimleri tarafından desteklenen yaygın özellikleri ve yöntemleri ekleyin.

Bu öğeler, temel bir özel öğe tanımına şu şekilde eklenir:

// 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);

Kaydettikten sonra, bu öğeyi tarayıcı tarafından sağlanan bir form kontrolü kullandığınız her yerde kullanabilirsiniz:

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

Değer ayarlama

attachInternals() yöntemi, form kontrolü API'lerine erişim sağlayan bir ElementInternals nesnesi döndürür. Bunların en temeli, kontrolün mevcut değerini belirleyen setFormValue() yöntemidir.

setFormValue() yöntemi, üç değer türünden birini alabilir:

  • Dize değeridir.
  • File nesnesi.
  • FormData nesnesi. Birden çok değer iletmek için bir FormData nesnesi kullanabilirsiniz (örneğin, bir kredi kartı giriş kontrolü bir kart numarası, son kullanma tarihi ve doğrulama kodu iletebilir).

Basit bir değer ayarlamak için:

this.internals_.setFormValue(this.value_);

Birden çok değer ayarlamak için aşağıdakine benzer bir işlem yapabilirsiniz:

// 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);

Giriş doğrulaması

Kontrolünüz, dahili nesne nesnesinde setValidity() yöntemini çağırarak form doğrulamasına da katılabilir.

// 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_);
}

Formla ilişkili bir özel öğeyi, yerleşik form kontrolünde olduğu gibi :valid ve :invalid sözde sınıflarıyla biçimlendirebilirsiniz.

Yaşam döngüsü geri çağırmaları

Formla ilişkili bir custom element API'si, form yaşam döngüsüyle bağlantılı bir dizi ekstra yaşam döngüsü geri çağırması içerir. Geri çağırmalar isteğe bağlıdır: Geri çağırma işlemini yalnızca öğenizin yaşam döngüsü içinde bir noktada yapması gerekiyorsa uygulayın.

void formAssociatedCallback(form)

Tarayıcı öğeyi bir form öğesiyle ilişkilendirdiğinde veya öğenin bir form öğesiyle ilişkisini kestiğinde çağrılır.

void formDisabledCallback(disabled)

Bu öğenin disabled özelliği eklendiğinden veya kaldırıldığından ya da bu öğenin üst öğesi olan bir <fieldset> üzerinde disabled durumu değiştiğinden disabled durumu değiştiğinden çağrılır. disabled parametresi, öğenin yeni devre dışı durumunu gösterir. Örneğin, bu öğe devre dışı bırakıldığında gölge DOM'sindeki öğeleri devre dışı bırakabilir.

void formResetCallback()

Form sıfırlandıktan sonra çağrılır. Öğe kendisini bir tür varsayılan duruma sıfırlamalıdır. <input> öğeleri için bu genellikle value özelliğinin işaretlemede ayarlanan value özelliğiyle eşleşecek şekilde ayarlanmasını (veya onay kutusu kullanılması durumunda, checked özelliğinin checked özelliğiyle eşleşecek şekilde ayarlanmasını) içerir.

void formStateRestoreCallback(state, mode)

Aşağıdaki iki durumdan birinde çağrılır:

  • Tarayıcı, öğenin durumunu geri yüklediğinde (örneğin, bir gezinmeden sonra veya tarayıcı yeniden başlatıldığında). Bu durumda mode bağımsız değişkeni "restore" şeklindedir.
  • Tarayıcının form otomatik doldurma gibi giriş yardımı özellikleri bir değer ayarladığında. Bu durumda mode bağımsız değişkeni "autocomplete" şeklindedir.

İlk bağımsız değişkenin türü, setFormValue() yönteminin nasıl çağrıldığına bağlıdır. Daha fazla bilgi için Form durumunu geri yükleme başlıklı makaleyi inceleyin.

Form durumu geri yükleniyor

Bazı durumlarda (ör. bir sayfaya geri giderken veya tarayıcıyı yeniden başlatırken), tarayıcı formu, kullanıcının bıraktığı duruma geri yüklemeyi deneyebilir.

Formla ilişkilendirilmiş özel öğeler için geri yüklenen durum, setFormValue() yöntemine ilettiğiniz değerlerden gelir. Bu yöntemi önceki örneklerde gösterildiği gibi tek bir değer parametresiyle veya iki parametreyle çağırabilirsiniz:

this.internals_.setFormValue(value, state);

value, kontrolün gönderilebilir değerini temsil eder. İsteğe bağlı state parametresi, kontrol durumunun dahili bir temsilidir. Bu parametre, sunucuya gönderilmeyen verileri içerebilir. state parametresi, value parametresiyle aynı türleri alır. Bir dize, File veya FormData nesnesi olabilir.

state parametresi, yalnızca değere dayalı olarak bir kontrolün durumunu geri yükleyemediğiniz durumlarda kullanışlıdır. Örneğin, birden fazla mod içeren bir renk seçici oluşturduğunuzu varsayalım: Bir palet veya RGB renk çemberi. Gönderilebilir değer, "#7fff00" gibi standart bir biçimde seçilen renk olur. Ancak denetimi belirli bir duruma geri yüklemek için hangi modda olduğunu da bilmeniz gerekir. Bu nedenle state, "palette/#7fff00" şeklinde görünebilir.

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

Kodunuzun, saklanan durum değerine göre durumunu geri yüklemesi gerekir.

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.
}

Daha basit bir kontrolde (örneğin, bir sayı girişi) bu değer, denetimi önceki durumuna geri yüklemek için muhtemelen yeterli olacaktır. setFormValue() öğesini çağırırken state öğesini çıkarırsanız değer formStateRestoreCallback() öğesine iletilir.

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

Çalışan bir örnek

Aşağıdaki örnekte formla ilişkilendirilmiş özel öğelerin birçok özelliği bir araya getirilmiştir. API'yi iş başında görmek için API'yi Chrome 77 veya sonraki bir sürümde çalıştırdığınızdan emin olun.

Özellik algılama

formdata etkinliği ve formla ilişkili özel öğelerin kullanılabilir olup olmadığını belirlemek için özellik algılamayı kullanabilirsiniz. Şu anda her iki özellik için de yayınlanmış bir çoklu dolgu yok. Her iki durumda da kontrolün değerini forma yaymak için gizli bir form öğesi eklemeye geri dönebilirsiniz. Formla ilişkili özel öğelerin daha gelişmiş özelliklerinin birçoğunun çoklu doldurmada zorlanması veya imkansız olması muhtemeldir.

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

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

Sonuç

formdata etkinliği ve formla ilişkili özel öğeler, özel form kontrolleri oluşturmak için yeni araçlar sağlar.

formdata etkinliği size yeni özellikler sunmaz ancak gizli bir <input> öğesi oluşturmak zorunda kalmadan form verilerinizi gönderme işlemine eklemeniz için bir arayüz sağlar.

Formla ilişkili özel öğeler API'si, yerleşik form kontrolleri gibi çalışan özel form kontrolleri yapmak için bir dizi yeni özellik sunar.

Oudom Pravat'ın Unsplash'teki lokomotif resmi.