Javascript İle Gerçek Zamanlı Arama Önerisi Nasıl Yapılır?

Javascript İle Gerçek Zamanlı Arama Önerisi Nasıl Yapılır?
Javascript İle Gerçek Zamanlı Arama Önerisi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Gerçek zamanlı bir arama sistemi kurmak için karmaşık kütüphanelere ihtiyacınız yoktur. Modern tarayıcıların desteklediği Fetch API ve ES6+ sözdizimi yeterlidir. Çalışma ortamınızda bir metin editörü (VS Code gibi) ve tarayıcı konsolu dışında herhangi bir özel kurulum gerekmez.

  • HTML5 ve CSS3: Arayüz yapısı ve stil düzenlemeleri için.
  • Modern JavaScript (ES6+): Veri işleme ve asenkron işlemler için.
  • JSON Veri Kaynağı: Önerilerin çekileceği bir API veya yerel veri dizisi.

Projenize başlamadan önce, projenizin kök dizininde bir index.html ve script.js dosyası oluşturduğunuzdan emin olun.

Adım 1: Arama Arayüzünü Oluşturma

Kullanıcının veri gireceği bir input alanı ve önerilerin görüntüleneceği bir ul (sırasız liste) yapısı kurmalıyız. Bu yapı, JavaScript ile dinamik olarak güncellenecektir.

    Burada autocomplete="off" özniteliğini kullanarak tarayıcının kendi varsayılan önerilerinin bizim özel arama önerilerimizle çakışmasını engelliyoruz.

    Adım 2: JavaScript ile Veri Yönetimi ve Olay Dinleyicileri

    Kullanıcı klavyeden bir tuşa bastığında (input olayı), arama kutusundaki değeri yakalamamız gerekir. Ancak, her tuş basımında sunucuya istek atmak performans kaybına yol açar. Bu yüzden "Debounce" tekniğini kullanacağız.

    const searchInput = document.getElementById('search-input');
    const suggestionsList = document.getElementById('suggestions-list');
    
    let debounceTimer;
    
    searchInput.addEventListener('input', (e) => {
      clearTimeout(debounceTimer);
      const query = e.target.value;
    
      debounceTimer = setTimeout(() => {
        fetchSuggestions(query);
      }, 300); // 300ms bekleme süresi
    });

    Yukarıdaki kod, kullanıcı yazmayı bırakana kadar 300 milisaniye bekler. Bu, gereksiz API isteklerini önlemek için kritik bir "performans optimizasyonu" yöntemidir.

    Adım 3: Asenkron Veri Çekme (Fetch API)

    Verileri bir veritabanından veya JSON dosyasından çekmek için fetch metodunu kullanırız. Güvenlik açısından, gelen veriyi doğrudan HTML içine basmamalı, textContent veya innerText kullanmalıyız.

    async function fetchSuggestions(query) {
      if (query.length < 2) {
        suggestionsList.innerHTML = '';
        return;
      }
    
      try {
        const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
        const data = await response.json();
        renderSuggestions(data);
      } catch (error) {
        console.error('Arama hatası:', error);
      }
    }

    encodeURIComponent kullanımı, arama sorgusundaki özel karakterlerin (örneğin &, ?, =) URL yapısını bozmasını engeller. Bu, XSS (Cross-Site Scripting) saldırılarına karşı temel bir savunmadır.

    Kritik Güvenlik Uyarısı: Kullanıcıdan gelen veriyi asla doğrudan innerHTML içine gömmeyin. Eğer önerileriniz HTML etiketi içeriyorsa, mutlaka bir sanitizasyon kütüphanesi (DOMPurify gibi) kullanın. Aksi takdirde XSS saldırılarına kapı aralamış olursunuz.

    Adım 4: Önerileri Ekrana Yazdırma (DOM Manipülasyonu)

    Çekilen verileri liste öğeleri (li) olarak oluşturup DOM'a eklemeliyiz. Bu adım, görsel geri bildirimin sağlandığı yerdir.

    function renderSuggestions(results) {
      suggestionsList.innerHTML = '';
      
      results.forEach(item => {
        const li = document.createElement('li');
        li.textContent = item.name;
        li.addEventListener('click', () => {
          searchInput.value = item.name;
          suggestionsList.innerHTML = '';
        });
        suggestionsList.appendChild(li);
      });
    }

    Burada her öneriye bir tıklama olayı ekleyerek, kullanıcının seçtiği önerinin otomatik olarak arama kutusuna dolmasını sağlıyoruz.

    Adım 5: Arama Yöntemlerinin Karşılaştırılması

    Aşağıdaki tablo, projenizde kullanabileceğiniz farklı arama yöntemlerinin artılarını ve eksilerini özetlemektedir.

    Yöntem Avantaj Dezavantaj
    İstemci Taraflı (Local) Çok hızlı, sunucu yükü yok Büyük verilerde tarayıcıyı yorar
    Sunucu Taraflı (API) Büyük veriler için ideal Gecikme (Latency) olabilir

    Sıkça Sorulan Sorular

    Debounce neden gereklidir?

    Kullanıcı "JavaScript" yazarken 10 tuşa basar. Debounce olmazsa 10 ayrı API isteği gider. Debounce ile sadece yazma bittiğinde tek bir istek gönderilir.

    Arama sonuçları neden boş dönüyor?

    API uç noktanızın (endpoint) doğru çalıştığından ve encodeURIComponent ile sorguyu doğru formatladığınızdan emin olun. Tarayıcı konsolunda "Network" sekmesini kontrol edin.

    Mobil cihazlarda klavye kapatma sorunu yaşanır mı?

    Öneriler listesi tıklandığında blur() metodu ile klavyeyi kapatmak, kullanıcı deneyimini artırır.

    Veri gizliliği nasıl korunur?

    Kullanıcı aramaları hassas bilgi içerebilir. API isteklerinizi mutlaka HTTPS üzerinden yapın.

    Bu yapı SEO uyumlu mudur?

    Bu bir kullanıcı arayüzü özelliği olduğu için doğrudan SEO'yu etkilemez, ancak hızlı çalışan bir arama kutusu "Core Web Vitals" metriklerine dolaylı katkı sağlar.

    Sorumluluk Reddi: Bu rehberde paylaşılan kodlar eğitim amaçlıdır. Üretim ortamında (production) kullanırken mutlaka sunucu tarafında girdi doğrulama (input validation) ve hız sınırlama (rate limiting) önlemleri alınız.

    İleri Seviye İpuçları: Klavye Navigasyonu ve Erişilebilirlik

    Kullanıcı deneyimini bir üst seviyeye taşımak için arama önerilerini sadece fare ile değil, klavye ile de yönetilebilir hale getirmelisiniz. Kullanıcı "aşağı" ve "yukarı" ok tuşlarını kullandığında öneriler arasında gezinebilmeli, "Enter" tuşuna bastığında ise seçili öneriye gitmelidir.

    Bunu başarmak için keydown olayını dinleyerek aktif öğeyi takip eden bir indeks değişkeni tutmanız gerekir:

    let activeIndex = -1;
    
    inputElement.addEventListener('keydown', (e) => {
        const items = document.querySelectorAll('.suggestion-item');
        if (e.key === 'ArrowDown') {
            activeIndex = (activeIndex + 1) % items.length;
            updateActiveStyle(items, activeIndex);
        } else if (e.key === 'ArrowUp') {
            activeIndex = (activeIndex - 1 + items.length) % items.length;
            updateActiveStyle(items, activeIndex);
        } else if (e.key === 'Enter' && activeIndex > -1) {
            items[activeIndex].click();
        }
    });
    
    function updateActiveStyle(items, index) {
        items.forEach(item => item.classList.remove('active'));
        if (items[index]) items[index].classList.add('active');
    }

    Performans Optimizasyonu: Sanal DOM ve Bellek Yönetimi

    Eğer arama önerileriniz binlerce satırlık bir veri setinden geliyorsa, her tuş vuruşunda DOM'u tamamen temizleyip yeniden oluşturmak tarayıcıyı yorabilir. Bu noktada "DocumentFragment" kullanarak DOM manipülasyonlarını tek bir işlemde gerçekleştirmek performansı ciddi oranda artırır.

    DOM Manipülasyonunda Verimlilik

    Aşağıdaki yöntem, her bir öneri için ayrı ayrı DOM güncellemesi yapmak yerine, tüm listeyi bellekte hazırlayıp tek seferde sayfaya ekler:

    function renderSuggestions(data) {
        const container = document.getElementById('suggestions-container');
        const fragment = document.createDocumentFragment();
        
        container.innerHTML = ''; // Önceki sonuçları temizle
    
        data.forEach(item => {
            const div = document.createElement('div');
            div.className = 'suggestion-item';
            div.textContent = item.name;
            fragment.appendChild(div);
        });
    
        container.appendChild(fragment); // Tek seferde DOM'a ekle
    }

    Hata Ayıklama (Debugging) Stratejileri

    Gerçek zamanlı aramalarda en sık karşılaşılan sorunlar "Race Condition" (yarış durumu) hatalarıdır. Örneğin, kullanıcı hızlıca "elma" yazdı ancak "e" harfi için yapılan geç istek, "elma" isteğinden daha sonra dönerse arayüzde yanlış sonuçlar görünebilir. Bunu engellemek için AbortController API'sini kullanmalısınız.

    let controller;
    
    async function fetchWithAbort(query) {
        if (controller) controller.abort(); // Önceki isteği iptal et
        controller = new AbortController();
        
        try {
            const response = await fetch(`/api/search?q=${query}`, { signal: controller.signal });
            return await response.json();
        } catch (err) {
            if (err.name === 'AbortError') console.log('İstek iptal edildi.');
        }
    }

    Bu yöntemle, kullanıcının en son yazdığı sorgunun her zaman öncelikli olmasını sağlar ve gereksiz ağ trafiğini engellersiniz.

    Sonuç

    JavaScript ile gerçek zamanlı arama önerisi yapmak, doğru teknikleri (debounce, asenkron veri çekme, DOM sanitizasyonu) birleştirdiğinizde oldukça verimli bir süreçtir. Bu rehberde öğrendiğiniz temel yapı üzerine, klavye ile navigasyon (ok tuşları ile öneriler arasında gezinme) veya vurgulama (highlighting) gibi özellikler ekleyerek sisteminizi geliştirebilirsiniz. Bir sonraki adım olarak, arama sonuçlarınıza "kategori bazlı filtreleme" özelliği eklemeyi deneyebilirsiniz.

    Bu yazıya tepkinizi paylaşın:
    Mert Demir

    Teknik beceriler ve bakım onarım rehberleri konusunda deneyimli bir editörüm. Okuyucularıma günlük hayatta tasarruf sağlayacak ipuçları sunuyorum.

    Yorumlar (0)

    Yorum Yaz