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ıya yerleşik olmayan denetimler sağlamak veya yerleşik form denetimleriyle mümkün olanın ötesinde görünüm ve izlenimi özelleştirmek için özel form denetimleri 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önderme işlemine 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önderme işlemine katılmasına izin verir. Böylece gizli bir <input> kullanmadan form verileri ekleyebilirsiniz.
  • Formla ilişkili özel öğeler API'si, özel öğelerin yerleşik form denetimlerine benzer şekilde hareket etmesini 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, herhangi bir JavaScript kodunun form gönderimine 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. Bir kullanıcı gönder düğmesini tıkladığında form, gönderilen tüm verileri barındıran 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 nasıl çalıştığını 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şkilendirilmiş ö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 kontrolleri, 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> öğesinin içine yerleştirdiğinizde, tarayıcı tarafından sağlanan 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 kontrollerinden birinde geçersiz giriş varsa form gönderilemez.
  • Bu öğ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 standart CSS sözde sınıflarını (ör. :disabled ve :invalid) destekler.

Çok fazla özellik var! 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şkili bir özel öğeye dönüştürmek için fazladan birkaç 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.
  • setFormValue() ve setValidity() gibi form denetimlerinde ekstra yöntemler ve özelliklere erişmek için öğ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 kullanacağı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 temel olanı, kontrolün mevcut değerini belirleyen 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ı, bazı durumlarda (ör. bir sayfaya geri gittiğinizde veya tarayıcıyı yeniden başlattığınızda) formu kullanıcının bıraktığı duruma geri yüklemeye çalışabilir.

Formla ilişkili bir özel öğ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. Bu parametre bir 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, birden çok modu olan bir renk seçici oluşturduğunuzu varsayalım: bir palet veya RGB renk çemberi. 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şkili ö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ğ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ış çoklu dolgu yok. 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.