Daha özellikli form kontrolleri

Yeni bir etkinlik ve özel öğeler API'leri sayesinde formlara katılmak artık çok daha kolay.

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 izlenimi ö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 alınan bazı özellikleri göz önünde bulundurun:

  • Giriş, formun kontrol listesine otomatik olarak eklenir.
  • Girişin değeri formla birlikte otomatik olarak gönderilir.
  • Giriş, form doğrulama işlemine katılır. :valid ve :invalid sözde sınıflarını kullanarak girişi biçimlendirebilirsiniz.
  • Form sıfırlandığında, form yeniden yüklendiğinde veya tarayıcı form girişlerini otomatik doldurmaya çalıştığında giriş bildirilir.

Özel form denetimleri genellikle bu özelliklerden birkaçına sahiptir. Geliştiriciler, form gönderme işlemine katılmak için forma gizli bir <input> eklemek gibi JavaScript'teki bazı sınırlamalara çözüm bulabilirler. Ancak diğer özellikler tek başına JavaScript'te çoğaltılamaz.

İki yeni web özelliği, özel form denetimleri oluşturmayı ve mevcut özel denetimlerin sınırlamalarını kaldırmayı kolaylaştırıyor:

  • 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önderme işlemine katılmasına izin veren alt düzey bir API'dir. Sistem şu şekilde çalışır:

  1. Etkileşimde bulunmak istediğiniz forma bir formdata etkinlik işleyicisi 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 dinleyicisi, form gönderilmeden önce veri ekleme veya değiştirme fırsatına sahip olur.

Aşağıda, formdata etkinlik işleyicide tek bir değer göndermeyle ilgili bir örnek 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);
});

Bunu, Glitch örneğimizi kullanarak 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

  • 5
  • 12
  • 4
  • 5

Kaynak

Formla ilişkili özel öğeler

Etkinliğe dayalı API'yi her tür bileşenle kullanabilirsiniz, ancak yalnızca gönderim işlemiyle etkileşim kurmanıza olanak tanır.

Standartlaştırılmış form denetimleri, gönderimin yanı sıra form yaşam döngüsünün birçok bölümünde yer alır. Formla ilişkili özel öğeler, özel widget'lar ile yerleşik kontroller arasındaki boşluğu kapatmayı amaçlar. Formla ilişkili özel öğeler, standartlaştırılmış form öğelerinin çoğu ö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ğeri ayarlayabilir.
  • Bu öğe, geçerli girişe sahip olup olmadığını gösteren 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.
  • Bu öğe, :disabled ve :invalid gibi form kontrolleri için standart CSS sözde sınıflarını destekler.

Çok fazla özellik var! Bu makalede hepsine değinilmeyecek ancak özel öğenizi bir formla entegre etmek için gereken temel bilgiler açıklanacaktı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.
  • name, value ve validity gibi form denetimlerinin desteklediği yaygın özellikleri ve yöntemleri ekleyin.

Bu öğeler, temel özel öğe tanımına şu şekilde 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 form kontrolünü 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 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 çok değer iletmek için FormData nesnesini kullanabilirsiniz (örneğin, 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ğı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ı

Ayrıca kontrolünüz, dahili nesnede setValidity() yöntemini çağırarak 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_);
}

Yerleşik bir form kontrolünde olduğu gibi, formla ilişkili bir özel öğenin stilini :valid ve :invalid sözde sınıflarıyla belirleyebilirsiniz.

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

Formla ilişkili özel öğe API'leri, form yaşam döngüsüyle bağlantı kurmak için bir dizi ekstra yaşam döngüsü geri çağırması içerir. Geri çağırma işlevleri isteğe bağlıdır: Yalnızca öğenizin yaşam döngüsünün o noktasında bir işlem yapması gerekiyorsa geri çağırma 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ğinin eklenmesi veya kaldırılması ya da bu öğenin üst öğesi olan bir <fieldset> üzerindeki disabled durumu değiştiği için öğenin disabled durumundan sonra çağrılır. disabled parametresi, öğenin yeni devre dışı durumunu temsil eder. Söz konusu öğe, örneğin 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. Öğenin kendisini bir tür varsayılan duruma sıfırlaması gerekir. <input> öğelerinde bu işlem genellikle, value özelliğinin işaretlemede ayarlanan value özelliğiyle eşleşecek şekilde ayarlanmasını (veya onay kutusu durumunda, checked özelliğinin checked özelliğiyle eşleşecek şekilde ayarlanmasını) içerir.

void formStateRestoreCallback(state, mode)

Şu iki durumdan birinde arandı:

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

İ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 durumu geri yükleniyor

Bir sayfaya geri gitme veya tarayıcıyı yeniden başlatma gibi bazı durumlarda tarayıcı, formu kullanıcının bıraktığı duruma geri yüklemeyi deneyebilir.

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

state parametresi, tek başına değeri temel alarak bir kontrolün durumunu geri yükleyemediğinizde 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, "#7fff00" gibi standart bir formdaki seçili renk olur. Ancak denetimi belirli bir duruma geri yüklemek için hangi modda olduğunu da bilmeniz gerekir. Bu nedenle state "palette/#7fff00" gibi görünebilir.

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

Kodunuzun, depolanan 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 sayı girişi gibi) değer, kontrolü önceki durumuna geri yüklemek için muhtemelen yeterlidir. setFormValue() çağrılırken state öğesini hariç tutarsanı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şkili özel öğelerin birçok özelliği bir arada yer almaktadır. 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.

Ö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, kontrolün değerini forma yaymak için gizli form öğesi ekleyebilirsiniz. Formla ilişkili özel öğelerin daha gelişmiş özelliklerinin birçoğuna çoklu dolgu yapmak büyük olasılıkla 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 bir özellik sunmaz ancak gizli bir <input> öğesi oluşturmanıza gerek kalmadan form verilerinizi gönderme işlemine eklemeniz için bir arayüz sunar.

Formla ilişkili özel öğeler API'si, yerleşik form denetimleri gibi çalışan özel form denetimleri oluşturmak için yeni özellikler sunar.

Oudom Pravat'ın Unsplash'teki hero resmi.