Vanilla JavaScript ile İnteraktif Kanban Panosu

Kanban Panosu — Teknik Dokümantasyon

🗂️ Vanilla JavaScript ile İnteraktif Kanban Panosu

Pointer Events tabanlı sürükle-bırak, localStorage kalıcılığı ve erişilebilirlik odaklı teknik dokümantasyon.

1 Özet

Bu doküman, harici bağımlılık kullanmadan Vanilla JavaScript ile geliştirilmiş, sürükle-bırak destekli interaktif bir Kanban panosu bileşeninin tüm teknik detaylarını içerir. Bileşen; sıfır bağımlılık, tam çevrimdışı çalışma, erişilebilir etkileşim ve kusursuz durum yönetimi prensiplerini bir arada barındırır.

2 Teknik Varsayımlar

  • Tarayıcı desteği: Modern evergreen tarayıcılar (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+). Pointer Events API ve CSS custom properties kullanılır.
  • Modül yapısı: Tek HTML dosyası içinde HTML + CSS + JS birleşik; harici bağımlılık yok.
  • Durum yönetimi: Uygulama durumu tek bir state nesnesinde tutulur ve her mutasyondan sonra localStorage'a senkronize edilir.
  • Sürükle-bırak: HTML5 Drag & Drop API yerine Pointer Events API tercih edilmiştir. Dokunmatik cihazlarda tutarlı çalışır.
  • Erişilebilirlik: Klavye ile kart taşıma (Alt + ←/→/↑/↓) desteklenir; aria-live ile ekran okuyucu bildirimleri sağlanır.
  • Güvenlik: Kullanıcı girdileri textContent üzerinden DOM'a yazılarak XSS riski minimize edilmiştir.
  • Performans: Liste render işlemlerinde DocumentFragment kullanılır; gereksiz reflow önlenir.

3 Mimari Genel Bakış

KatmanSorumluluk
Statecolumns, cards verilerinin tek kaynaktan yönetimi
PersistencelocalStorage ile otomatik kayıt/yükleme
RendererDOM oluşturma ve senkronizasyon
DragControllerPointer tabanlı sürükle-bırak yaşam döngüsü
AccessibilityKlavye navigasyonu ve ARIA duyuruları

4 Kritik Uygulama Detayları

4.1 Pointer Events ile Sürükle-Bırak

HTML5 Drag & Drop API, mobil cihazlarda ve özelleştirilmiş görsel katmanlarda yetersiz kaldığı için Pointer Events API tercih edilmiştir. Sürükleme yaşam döngüsü üç aşamadan oluşur:

  • pointerdown → Hayalet klon (ghost) ve yer tutucu (placeholder) oluşturulur.
  • pointermove → Klon imleci takip eder; elementFromPoint ile hedef liste belirlenir; yer tutucu doğru indekse taşınır.
  • pointerup → Yer tutucunun nihai indeksi hesaplanır, moveCard() çağrılır ve tüm görsel katmanlar temizlenir.

setPointerCapture sayesinde imleç karttan çıksa bile olay akışı kesilmez.

4.2 Durum Tutarlılığı

moveCard() fonksiyonu, aynı sütun içinde aşağı yönde taşıma durumunda oluşan klasik index shift hatasını (fromIndex < toIndex ise insertIndex - 1) açıkça ele alır.

4.3 Erişilebilirlik (a11y)

  • Her kart role="button" ve tabIndex=0 ile odaklanabilir.
  • Alt + Ok kombinasyonu ile kartlar sütunlar arası veya sütun içi taşınabilir.
  • aria-live="polite" bölgesi ile ekran okuyuculara "Kart taşındı", "Kart silindi" gibi bildirimler iletilir.
  • Sütun başlıkları contentEditable olup Enter/Escape ile düzenlenir.

4.4 Güvenlik Önlemleri

  • Kullanıcı metinleri yalnızca textContent üzerinden yazılır → XSS imkânsız.
  • innerHTML hiç kullanılmamıştır.
  • Metin uzunluğu MAX_TEXT_LENGTH = 200 karakter ile sınırlandırılır.
  • localStorage yükleme sırasında şema doğrulaması yapılır; bozuk veri sessizce sıfırlanır.

4.5 Performans

  • Liste render işlemlerinde DocumentFragment kullanılır; tek bir reflow ile DOM'a bağlanır.
  • Yalnızca değişen sütun (renderColumnCards) yeniden çizilir; tüm pano yeniden oluşturulmaz.
  • requestAnimationFrame ile odak geri kazanımı bir sonraki boyama döngüsüne ertelenir.

5 Önceki Sürümlerdeki Hatalar ve Düzeltmeleri

#HataNedenDüzeltme
1 updatePlaceholder içinde y tanımsız Fonksiyon kapsamında değişken yok → ReferenceError Fonksiyon sadeleştirildi; ghost merkez Y değeri kullanılıyor
2 this bağlamı kayboluyordu Ok fonksiyonları addEventListener'a doğrudan verildi _onMove / _onUp içinde Drag global referansı kullanılıyor
3 Ghost li olarak klonlanıyordu card-ghost sınıfı card sınıfını da taşıyordu Ghost artık sade div; yalnızca metin ve stiller klonlanıyor
4 Kartlar hiç görünmüyordu Seed verisi kontrolü localStorage.getItem yokluğuna bağlıydı; başlatma akışı hatalıydı createInitialState() içinde seed üretiliyor; state = load() || createInitialState()
5 Sütun yüksekliği çöküyordu .column yüksekliği ebeveyn board'un align-items davranışıyla kısıtlanmıyordu .board { align-items: flex-start } ve .column { display: flex; flex-direction: column } sabitlendi
Önemli: Eski kanban.board.v1 veya kanban.board.v2 anahtarları yeni sürümde kullanılmaz; kanban.board.v3 anahtarı ile temiz başlar. Sorun devam ederse DevTools → Application → Local Storage üzerinden eski anahtarları silin.

6 Kullanım Kılavuzu

EylemYöntem
Kart taşımaFare/dokunma ile sürükle
Kart taşıma (klavye)Kart odaktayken Alt + ←/→ (sütunlar arası), Alt + ↑/↓ (sıralama)
Kart düzenlemeÇift tık veya Enter
Kart silmeKart odaktayken Delete
Kart eklemeSütun altındaki + Kart ekle butonu
Sütun adı düzenlemeBaşlığa tıkla, yaz, Enter
KaydetmeOtomatik (her mutasyonda localStorage)

7 Sonuç

Sunulan çözüm; sıfır bağımlılık, tam çevrimdışı çalışma, erişilebilir etkileşim ve kusursuz durum yönetimi prensiplerini bir arada barındıran üretim kalitesinde bir Kanban bileşenidir. Modüler iç mimarisi sayesinde Persistence katmanı kolayca bir API istemcisiyle, DragController ise sıralama kütüphaneleriyle değiştirilebilir.

Yazınızı veya özel HTML kodunuzu buraya ekleyebilirsiniz...

Yazınızı veya özel HTML kodunuzu buraya ekleyebilirsiniz...