Daha özellikli form kontrolleri

Yeni bir etkinlik ve özel öğe API'leri sayesinde formlara katılım çok daha kolay hale geldi.

Arthur Evans

Birçok geliştirici, tarayıcıda yerleşik olmayan kontroller sağlamak veya yerleşik form kontrolleriyle mümkün olanın ötesinde bir görünüm ve his sağlamak için özel form kontrolleri oluşturur.

Ancak yerleşik HTML form kontrollerinin özelliklerini kopyalamak zor olabilir. Bir <input> öğesini forma eklediğinizde otomatik olarak sahip olduğu özelliklerden bazıları şunlardır:

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

Özel form denetimlerinde genellikle bu özelliklerden birkaçı bulunur. Geliştiriciler, JavaScript'teki bazı sınırlamaların üstesinden gelebilir. Örneğin, form gönderimine katılmak için bir forma gizli <input> ekleyebilirler. Ancak diğer özellikler yalnızca JavaScript'te kopyalanamaz.

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

  • formdata etkinliği, rastgele bir JavaScript nesnesinin form gönderimine katılmasına olanak tanır. Böylece, gizli bir <input> kullanmadan form verileri ekleyebilirsiniz.
  • Formla ilişkilendirilmiş özel öğeler API'si, özel öğelerin yerleşik form kontrolleri gibi davranmasına olanak tanır.

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

Etkinliğe dayalı API

formdata etkinliği, herhangi bir JavaScript kodunun form gönderme işlemine katılmasına olanak tanıyan düşük düzey bir API'dir. Bu mekanizma şu şekilde işler:

  1. Etkileşimde bulunmak istediğiniz forma bir formdata etkinlik dinleyicisi eklersiniz.
  2. Kullanıcı gönder düğmesini tıkladığında form, gönderilen tüm verileri içeren bir FormData nesnesi içeren bir formdata etkinliği tetikler.
  3. Her formdata dinleyici, form gönderilmeden önce verilere ekleme veya verileri değiştirme şansı elde eder.

Aşağıda, formdata etkinlik dinleyicisine tek bir değer gönderme örneği verilmiştir:

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

Tarayıcı uyumluluğu

Tarayıcı desteği

  • Chrome: 5.
  • Edge: 12.
  • Firefox: 4.
  • Safari: 5.

Kaynak

Formla ilişkili özel öğeler

Etkinlik tabanlı API'yi her tür bileşenle kullanabilirsiniz ancak yalnızca gönderim süreciyle etkileşim kurmanıza olanak tanır.

Standartlaştırılmış form denetimleri, göndermenin yanı sıra form yaşam döngüsünün birçok bölümünde yer alır. Formla ilişkilendirilmiş özel öğeler, özel widget'lar ile yerleşik kontroller arasındaki boşluğu doldurmayı amaçlar. Formla ilişkilendirilmiş özel öğeler, standartlaştırılmış form öğelerinin birçok özelliğiyle eşleşir:

  • Formla ilişkilendirilmiş bir özel öğeyi <form> içine yerleştirdiğinizde, tarayıcı tarafından sağlanan bir kontrol gibi otomatik olarak formla ilişkilendirilir.
  • Öğe, <label> öğesi kullanılarak etiketlenebilir.
  • Öğe, formla birlikte otomatik olarak gönderilen bir değer ayarlayabilir.
  • Öğe, geçerli girişe sahip olup olmadığını belirten bir işaret ayarlayabilir. Form denetimlerinden birinde geçersiz giriş varsa form gönderilemez.
  • Öğe, form yaşam döngüsünün çeşitli aşamalarında (ör. form devre dışı bırakıldığında veya varsayılan durumuna sıfırlandığında) geri çağırma işlevi sağlayabilir.
  • Öğe, form kontrolleri için standart CSS sözde sınıflarını (ör. :disabled ve :invalid) destekler.

Bu çok fazla özellik! Bu makalede bunların hepsi ele alınmasa da özel öğenizi bir formla entegre etmek için gereken temel bilgiler açıklanmaktadır.

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

Özel bir öğeyi formla ilişkilendirilmiş özel bir öğeye dönüştürmek için birkaç ek adım gerekir:

  • Özel öğe sınıfınıza statik bir formAssociated özelliği ekleyin. Bu, tarayıcıya öğeyi bir form kontrolü gibi işlemesini söyler.
  • Form kontrolleri için setFormValue() ve setValidity() gibi ek yöntemlere ve özelliklere erişmek üzere öğede attachInternals() yöntemini çağırın.
  • Form denetimleri tarafından desteklenen ortak özellikleri ve yöntemleri (ör. name, value ve validity) ekleyin.

Bu öğeler, temel bir özel öğe tanımına nasıl uyar?

// 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 denetimini 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 denetimi API'lerine erişim sağlayan bir ElementInternals nesnesi döndürür. Bunlardan en temeli, kontrolün mevcut değerini ayarlayan setFormValue() yöntemidir.

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

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

Basit bir değer ayarlamak için:

this.internals_.setFormValue(this.value_);

Birden fazla değer ayarlamak için aşağıdaki gibi 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, internals nesnesinde setValidity() yöntemini çağırarak da form doğrulamasına 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şkilendirilmiş özel bir öğeye, yerleşik bir form denetimi gibi :valid ve :invalid sözde sınıflarıyla stil uygulayabilirsiniz.

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

Formla ilişkilendirilmiş özel öğe API'si, form yaşam döngüsü ile ilişkilendirilecek ek yaşam döngüsü geri çağırma yöntemi grubu içerir. Geri çağırmalar isteğe bağlıdır: Yalnızca öğenizin yaşam döngüsünün o noktasında bir şey yapması gerekiyorsa geri çağırma uygulayın.

void formAssociatedCallback(form)

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

void formDisabledCallback(disabled)

Öğenin disabled durumu, bu öğenin disabled özelliği eklendiği veya kaldırıldığı ya da bu öğenin atası olan bir <fieldset> öğesinde disabled durumu değiştiği için değiştikten sonra çağrılır. disabled parametresi, öğenin yeni devre dışı durumunu temsil eder. Örneğin, öğe devre dışı bırakıldığında gölge DOM'undaki öğ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 işlem genellikle value özelliğinin, işaretlemede ayarlanan value özelliğiyle eşleşmesini (veya onay kutusu söz konusu olduğunda checked özelliğinin checked özelliğiyle eşleşmesini) içerir.

void formStateRestoreCallback(state, mode)

Aşağıdaki durumlardan birinde çağrılır:

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

İ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 bölümüne bakın.

Form durumunu geri yükleme

Tarayıcı, bir sayfaya geri dönerken veya tarayıcıyı yeniden başlatırken gibi bazı durumlarda formu kullanıcının bıraktığı duruma geri yüklemeyi deneyebilir.

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

this.internals_.setFormValue(value, state);

value, denetimin gönderilebilir değerini temsil eder. İsteğe bağlı state parametresi, denetimin durumunun dahili bir temsilidir ve sunucuya gönderilmeyen veriler içerebilir. state parametresi, value parametresiyle aynı türleri alır. Dize, File veya FormData nesnesi olabilir.

state parametresi, bir kontrolün durumunu yalnızca değere göre geri yükleyemediğiniz durumlarda kullanışlıdır. Örneğin, palet veya RGB renk tekerleği gibi birden fazla modu olan bir renk seçici oluşturduğunuzu varsayalım. Gönderilebilir value, seçilen rengi "#7fff00" gibi standart bir biçimde belirtir. Ancak kontrolü belirli bir duruma geri yüklemek için hangi modda olduğunu da bilmeniz gerekir. Bu nedenle durum "palette/#7fff00" gibi görünebilir.

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

Kodunuzun, durumunu depolanan durum değerine göre 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 kontrol söz konusu olduğunda (ör. sayı girişi), değeri kontrol panelini önceki durumuna geri yüklemek için yeterli olabilir. setFormValue() işlevini çağırırken state öğesini atlarsanız değer formStateRestoreCallback() işlevine 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'nin işleyişini görmek için Chrome 77 veya sonraki bir sürümde çalıştırdığınızdan emin olun.

Özellik algılama

formdata etkinliğinin ve formla ilişkili özel öğelerin kullanılıp kullanılamayacağını belirlemek için özellik algılamayı kullanabilirsiniz. Şu anda bu özellikler için yayınlanmış polyfill yoktur. Her iki durumda da, denetimin değerini forma iletmek için gizli bir form öğesi ekleyebilirsiniz. Formla ilişkili özel öğelerin daha gelişmiş özelliklerinin çoğunun polyfill olarak doldurulması muhtemelen zor veya imkansız olacaktır.

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şkilendirilmiş ö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 sürecine eklemek için bir arayüz sağlar.

Formla ilişkilendirilmiş özel öğeler API'si, yerleşik form denetimleri gibi çalışan özel form denetimleri oluşturmak için yeni bir özellik grubu sağlar.

Unsplash'taki Oudom Pravat tarafından oluşturulan hero resim.