Verdiğiniz JavaScript kurs müfredat dosyasını belirttiğiniz tüm kurallara uyarak (dakika/saniye bilgilerini kaldırarak, "Önizleme" ve "ders" satırlarını ele dışı bırakarak) satır satır ve en detaylı şekilde incelemeye başlıyoruz.

Bu rehber, bir yazılımcı dostunuz olarak konularda hiçbir özet yapmadan, her bölüme özel teorik açıklamalar, günlük hayat benzetmeleri (analojiler), kod örnekleri ve uygulamalar içerir.


🎯 "Öğrenecekleriniz" Bölümünün Detaylı Açıklaması

Kursun başındaki bu giriş kısmı, bir öğrencinin bu eğitimi bitirdiğinde hangi becerilere sahip olacağını gösteren bir yol haritasıdır. Maddeleri tek tek inceleyelim:


📚 KURS İÇERİĞİNİN SATIR SATIR ETKİNLİK VE KOD ANALİZİ

Bölüm 1: JavaScript Kursuna ve İçeriğe Giriş (JavaScript Uygulamaları İnşa Etme)

Bu bölüm, yazılım dünyasına adım attığınız yerdir. Gerekli araç gereçleri bilgisayarımıza kurup ilk kod ortamını hazırlarız.

Kod Editörü Visual Studio Code (VS Code)

Yazılımcıların kodlarını yazdığı, onlara renkler ve ipuçları vererek kolaylık sağlayan gelişmiş bir gelişmiş not defteridir. Tıpkı bir terzinin dikiş makinesi gibidir.

💻 Uygulama: Bilgisayara VS Code kurulur. JavaScript kodlarını tarayıcıda canlı görmek için "Live Server" eklentisi aktif edilir.


Bölüm 2: JavaScript Temel Esasları ve JavaScript ile Başlarken

Burası programlamanın temel yapı taşlarını öğrendiğimiz mutfaktır.

JavaScript Web'e Güç Verir & JavaScript Hakkında Daha Fazlası

JavaScript, web sayfalarını hareket ettiren beyindir. HTML kemik (yapı), CSS elbise (görünüm), JavaScript ise kas sistemidir (hareket).

JavaScript ve HTML Oluşturma & JavaScript Yorum Satırları (Comments)

JavaScript kodları HTML sayfasına <script> etiketi ile bağlanır. Yorum satırları ise bilgisayarın okumadığı, sadece yazılımcıların kendilerine not bırakmak için kullandığı alanlardır. Tıpkı bir yemek tarifinin kenarına "burayı çok pişirme" notu düşmek gibidir.

// Bu tek satırlık bir yorumdur. Bilgisayar burayı görmezden gelir.
/* 
   Bu ise çok satırlı 
   bir yorum alanıdır.
*/

JavaScript Değişkenleri (Variables - Let, Const) ve Veri Tipleri

Değişkenler, bilgisayarın hafızasındaki küçük kutulardır. İçlerine bilgi koyarız.

Günlük Hayat Analojisi: Bir yolcunun yaşı her yıl değiştiği için let ile tutulur, T.C. kimlik numarası hiç değişmediği için const ile tutulur.

let kullaniciYasi = 25; 
kullaniciYasi = 26; // Bu işlem yapılabilir.

const dogumYili = 1999;
// dogumYili = 2000; // HATA! Sabit değer değiştirilemez.

JavaScript Prompt ve Template Literal

💻 Uygulama: Kullanıcıdan ismini alıp ona özel karşılama mesajı hazırlayalım.

let isim = prompt("Lütfen adınızı giriniz:");
// Eski yöntem (ES5):
console.log("Merhaba " + isim + ", hoş geldin!");
// Modern yöntem (Template Literal - ES6+):
console.log(`Merhaba ${isim}, hoş geldin!`);

JavaScript Operatörleri ve Karşılaştırma Operatörleri

Matematiksel işlemler (+, -, *, /) ve durum karşılaştırmaları yapmak için kullanılır.

let sayi = 5;
let yazi = "5";

console.log(sayi == yazi);  // true (Çünkü ikisi de özünde 5)
console.log(sayi === yazi); // false (Çünkü biri sayı, biri yazı tipinde)

If Else Yapıları, Ternary Operatörü ve Switch Durumları

Karar verme mekanizmalarıdır. "Eğer hava yağmurluysa şemsiye al, değilse gözlük al" mantığıyla çalışır. Ternary, tek satırda hızlıca if-else yazma yoludur.

// Normal If-Else
let yas = 18;
if (yas >= 18) {
    console.log("Ehliyet alabilirsin.");
} else {
    console.log("Yaşın henüz küçük.");
}

// Ternary Operatörü Kullanımı
let mesaj = (yas >= 18) ? "Ehliyet alabilirsin." : "Yaşın henüz küçük.";
console.log(mesaj);

JavaScript Fonksiyonları (Parameters, Return, Arrow Functions)

Fonksiyonlar, tekrar tekrar kullanılabilen küçük fabrikalardır. İçine hammadde (parametre) verirsiniz, size işlenmiş ürün (return) verir. Arrow Function ise modern, kısa yazım şeklidir.

// Modern Arrow Function ve Return Kullanımı
const kareAl = (sayi) => {
    return sayi * sayi;
};

let sonuc = kareAl(4); // sonuc kutusuna 16 yüklenir.
console.log(sonuc);

Fonksiyon Kapsamı (Scope) ve Rekürsiyon (Recursion)

// Rekürsiyon ile Faktöriyel Hesaplama Örneği
function faktoriyel(n) {
    if (n === 1) return 1; // Durma noktası
    return n * faktoriyel(n - 1);
}
console.log(faktoriyel(5)); // 120 sonucunu verir.

IIFE (Immediately Invoked Function Expression)

Tanımlandığı anda kendiliğinden hemen çalışan, isim verilmesi zorunlu olmayan gizli fonksiyonlardır. Sayfa açılır açılır çalışmasını istediğimiz ayarlar için kullanılır.

(function() {
    console.log("Sayfa yüklendi, bu fonksiyon hemen çalıştı!");
})();

JavaScript Objeleri (Objects) ve Diziler (Arrays)

// Dizi Örneği
let meyveler = ["Elma", "Armut", "Muz"];

// Obje Örneği
let araba = {
    marka: "Toyota",
    model: "Corolla",
    yil: 2022,
    calis: function() {
        console.log("Araba çalıştı...");
    }
};
console.log(araba.marka); // Toyota
araba.calis(); // Objeye ait fonksiyonu çalıştırır.

Gelişmiş Dizi Yöntemleri (Filter, Map ve Döngüler)

Diziler üzerinde arama yapmak, her elemanı değiştirmek veya listeyi dönmek için kullanılan modern süper güçlerdir.

let sayilar =;

// Sadece çift sayıları filtreleyelim
let ciftler = sayilar.filter(sayi => sayi % 2 === 0); // [2, 4, 6]

// Her sayının iki katını alalım
let ikiKati = sayilar.map(sayi => sayi * 2); // [2, 4, 6, 8, 10, 12]

Bölüm 3: JavaScript Web Sayfası Etkileşimi ve Dinamik İçerik (DOM)

Bu bölümde ekran üzerindeki HTML elementlerini kontrol etmeyi, değiştirmeyi öğreniyoruz.

Element Seçimi (Selection) ve Yönetimi (Manipulation)

JavaScript'in HTML elementini bulması işlemine seçim denir. Onu değiştirmesine ise manipülasyon denir.

// HTML'deki id'si "baslik" olan yazıyı seçelim ve rengini değiştirelim
const anaBaslik = document.querySelector("#baslik");
anaBaslik.textContent = "Yeni Başlık JavaScript ile Yazıldı!";
anaBaslik.style.color = "red"; // Yazıyı kırmızı yapar.

Çocuk Öğeler ve DOM Ağacında Gezinme (Traversing)

HTML elementleri birbirinin içinde yaşar (Anne/Baba ve Çocuk ilişkisi). Bir elementi seçip onun üst sınırına (Parent) veya içindeki alt öğelerine (Children) kodla ulaşabiliriz.

Olay Dinleyiciler (Event Listeners) ve Olay Nesnesi (Event Object)

Kullanıcının yaptığı tıklama, fareyi oynatma veya klavyeden bir tuşa basma hareketlerine "Event" (Olay) denir. JavaScript bu hareketleri pusuya yatarak dinler.

💻 Uygulama: Bir butona basıldığında sayfada yeni bir liste elemanı oluşturalım.

const buton = document.querySelector("#ekleButon");
const liste = document.querySelector("#benimListem");

buton.addEventListener("click", function(event) {
    // event nesnesi tıklamanın nerede yapıldığı bilgisini taşır.
    const yeniMadde = document.createElement("li"); // Havadan li elementi oluşturduk.
    yeniMadde.textContent = "Yeni Alışveriş Maddesi";
    liste.appendChild(yeniMadde); // Listeye ekledik.
});

Olay Kabarcıklanması (Event Bubbling) ve Yakalama (Capturing)

Bir elemente tıkladığınızda, o element bir kutunun içindeyse, tıklama olayı dıştaki kutuya da bulaşır. Buna kabarcıklanma denir. Sudan çıkan bir hava kabarcığının yukarı doğru çıkması gibidir. event.stopPropagation() ile bu bulaşma engellenebilir.


Bölüm 4: İleri Düzey JavaScript Yöntemleri ve Fazlası

Sayı ve Metin Metotları (String / Number Methods)

Yazıları parçalamak, büyütmek veya sayıları yuvarlamak için hazır fonksiyonlardır.

let rastgeleSayi = Math.floor(Math.random() * 10) + 1; // 1 ile 10 arasında rastgele tam sayı üretir.

JSON Parse ve Stringify & LocalStorage

let kullanici = { isim: "Ahmet", skor: 90 };

// Hafızaya kaydetme
localStorage.setItem("aktifKullanici", JSON.stringify(kullanici));

// Hafızadan geri okuma
let gelenVeri = localStorage.getItem("aktifKullanici");
let orjinalObje = JSON.parse(gelenVeri);
console.log(orjinalObje.isim); // Ahmet

Zamanlayıcılar (Timers - SetTimeout, SetInterval)

// 3 saniye sonra ekrana uyarı ver
setTimeout(() => {
    console.log("3 saniye doldu!");
}, 3000);

Düzenli İfadeler (RegEx - Regular Expressions)

Bir yazı bloğunun içinde belirli kurallara uyan kelimeleri arama yöntemidir. Örneğin girilen metnin gerçekten bir e-posta adresi olup olmadığını denetlemek için kullanılır.

Hata Yakalama (Try and Catch)

Yazdığımız kodda ani bir hata oluştuğunda programın tamamen çökmesini engelleyen güvenlik duvarıdır.

try {
    // Hata yapma ihtimali olan kod bloğu
    olmayanFonksiyonuCalistir();
} catch (error) {
    // Hata olursa burası çalışır ve hata mesajını basar, kod çökmez.
    console.log("Bir hata meydana geldi: " + error.message);
}

Asenkron Yapılar: XHR ve Fetch

İnternetteki başka bir sunucudan sayfayı yenilemeden canlı veri çekme (Hava durumu verisi, döviz kuru vb.) teknolojisidir. Modern dünyada fetch yöntemi kullanılır.

// Fetch ile internetten veri çekme örneği
fetch("https://github.com")
    .then(cevap => cevap.json())
    .then(veri => console.log(`Kullanıcı Adı: ${veri.name}`))
    .catch(hata => console.log("Hata oldu:", hata));

Bölüm 5: JavaScript HTML5 Canvas Giriş

Canvas, web sayfasında üzerine kodla resim, çizgi ve animasyon yapabileceğimiz boş bir dijital tuvaldir. Oyun grafikleri burada çizilir.

💻 Uygulama: Canvas üzerinde hareket eden bir kutu yapımı ve requestAnimationFrame kullanımı. requestAnimationFrame, tarayıcının ekran yenileme hızına göre animasyonları akıcı oynatan modern bir motordur.

const canvas = document.querySelector("#oyunAlani");
const ctx = canvas.getContext("2d");

let x = 10;
function animasyon() {
    ctx.clearRect(0, 0, canvas.width, canvas.height); // Ekranı temizle
    ctx.fillStyle = "blue";
    ctx.fillRect(x, 50, 50, 50); // Mavi kutu çiz
    x += 2; // Kutuyu sağa kaydır
    requestAnimationFrame(animasyon); // Sürekli tekrarla
}
// animasyon(); // Tetiklendiğinde kutu kaymaya başlar.

Bölüm 6'dan İtibaren Kurs İçi Projelerin ve Uygulamaların Birebir Yapılışı

Bu kısımdan sonra kurs içerisindeki uygulamaların çalışma mantıklarını kurup kod bloklarını ayağa kaldırıyoruz.

📌 Proje 1: Bahşiş Hesaplayıcı (Tip Calculator)

Kullanıcının hesap tutarını ve memnuniyet oranını girerek ödenecek bahşişi anlık hesapladığı uygulamadır.

// Mantık: Tutar ile oran çarpılır, kişi sayısına bölünür.
function bahsisHesapla(tutar, servisYuzdesi) {
    return tutar * (servisYuzdesi / 100);
}
console.log(`Ödenecek Bahşiş: ${bahsisHesapla(500, 15)} TL`); // 75 TL

📌 Proje 2: Karşılama Mesajları (Zamana Göre Değişen Dinamik Karşılayıcı)

Kullanıcının bilgisayar saatine bakar. Sabah ise "Günaydın", akşam ise "İyi Akşamlar" yazar ve sayfa arka plan rengini zamana göre gece moduna veya gündüz moduna alır.

const saat = new Date().getHours();
let mesaj = "";
if (saat < 12) {
    mesaj = "Günaydın!";
    document.body.style.backgroundColor = "#lightyellow";
} else if (saat < 18) {
    mesaj = "Tünaydın!";
} else {
    mesaj = "İyi Akşamlar!";
    document.body.style.backgroundColor = "#2c3e50"; // Gece koyuluğu
}
console.log(mesaj);

📌 Proje 3: Yazı Tura Uygulaması (Coin Toss)

Bilgisayarın Math.random ile 0 veya 1 seçerek "Yazı" veya "Tura" tahmini yaptığı oyun simülasyonudur.

function yaziTuraAt() {
    let sonuc = Math.random() < 0.5 ? "Yazı" : "Tura";
    console.log(`Para döndü ve geldi: ${sonuc}`);
}
yaziTuraAt();

📌 Proje 4: Taş Kağıt Makas Oyunu (Rock Paper Scissors)

Kullanıcının seçimi ile bilgisayarın rastgele ürettiği seçimi karşılaştırıp kazananı skor tablosuna işleyen sistemdir.

const secenekler = ["Taş", "Kağıt", "Makas"];
let bilgisayarSecimi = secenekler[Math.floor(Math.random() * 3)];
console.log(`Bilgisayarın Hamlesi: ${bilgisayarSecimi}`);

📌 Proje 5: Gelişmiş Zar Oyunu (Ultimate Dice Game)

JavaScript ile sayfada hiç zar yokken HTML elementlerini (document.createElement) kullanarak kare şeklinde zarlar oluşturur ve içine gelen sayı kadar küçük noktalar (daireler) çizer.

📌 Proje 6: Ses Çalar Uygulaması (Play Audio Files)

Sayfada birden fazla buton oluşturur. Her butona tıklandığında ona tanımlı olan kısa mp3 ses dosyasını (örneğin alkış, davul, hata sesi) oynatır.

let ses = new Audio("alkis.mp3");
// ses.play(); // Sesi çalar.

📌 Proje 7: Dinamik Klavye Hız Testi (Dynamic Typing Test)

Ekrana rastgele uzun bir cümle getirir. Kullanıcı yazmaya başladığı an Date nesnesi ile zamanı tutar. Kullanıcı cümleyi bitirdiğinde geçen süreyi hesaplayıp dakika başına kelime hızını (WPM) ölçer.

📌 Proje 8: Nesne Hareket Ettirici (Functionator / DOM Mover)

Klavyedeki yön tuşlarını (keydown olayı) dinleyerek ekrandaki bir karakter resmini veya kutuyu duvar sınırlarına çarpmayacak şekilde oyun alanında yürütür.

📌 Proje 9: Sihirli 8 Top (Magic Eight Ball)

Kullanıcı bir soru yazar, butona tıklar. İçerideki dizi içerisinden rastgele ("Kesinlikle evet", "Daha sonra tekrar dene", "Asla") gibi cevaplar ekrana animasyonla gelir.

📌 Proje 10: JavaScript Akordeon Menü (Accordion Project)

Sıkça sorulan sorular sayfalarında gördüğümüz, başlığa tıklayınca altındaki cevabın aşağı doğru süzülerek açıldığı, diğer açık olanların ise kapandığı sistemdir. classList.toggle("active") komutuyla yapılır.

📌 Proje 11: Şifre Kırıcı / Kasa Tahmin Oyunu (Combination Guesser)

Bilgisayar gizli bir 3 basamaklı sayı tutar. Kullanıcı yukarı aşağı butonlarıyla sayıları tahmin etmeye çalışır. JavaScript "Daha büyük bir sayı gir" veya "Kilit açıldı" şeklinde ipuçları basar.

📌 Proje 12: Kelime Karıştırma Oyunu (Word Scramble)

Bir kelimenin harflerinin yerini karmaşık hale getirir (Array.sort(() => Math.random() - 0.5)). Kullanıcıdan orijinal kelimeyi tahmin etmesini ister.

📌 Proje 13: Geri Sayım Sayacı (Countdown Timer)

Belirli bir gelecekteki tarihe (Örneğin yılbaşı gecesine) ne kadar gün, saat, dakika ve saniye kaldığını matematiksel hesaplamalarla her saniye ekranda günceller.

📌 Proje 14: Hafıza / Desen Eşleştirme Oyunu (Pattern Matching Game)

Ekranda renkli kutular sırayla yanar söner (Simond Says oyunu gibi). Kullanıcıdan bilgisayarın yaktığı sırayı hafızasında tutup aynı düzende kutulara tıklaması istenir.

📌 Proje 15: Balon Patlatma Oyunu (Click Popper Game)

Ekranda rastgele yerlerde ve hızlarda sürekli balonlar veya ikonlar belirir. Kullanıcı süre bitmeden onlara tıklayarak patlatmaya ve skor toplamaya çalışır.

📌 Proje 16: Yüksek Alçak Kart Oyunu (Hi Lo Card Game)

Deste oluşturulur. Ekrandaki karttan bir sonraki çekilecek kartın değerinin daha mı yüksek daha mı düşük olacağını tahmin etme oyunudur.

📌 Proje 17: Araba Yarışı Oyunu (Car Driving Game)

Tamamen JavaScript ile yapılmış dikey bir oyundur. Yukarı ok tuşuyla araba hızlanır, yol şeritleri aşağı doğru kayarak hareket illüzyonu yaratır. Karşıdan gelen arabalara çarpma kontrolü (Collision Detection) mesafeler ölçülerek yapılır.

📌 Proje 18: Karakter Sayacı (Character Counter)

Bir mesaj kutusuna (textarea) yazı yazıldıkça kaç harf hakkı kaldığını söyler. Sınır yaklaştığında yazıyı sarıya, sınır bittiğinde kırmızıya boyar ve yazmayı engeller.

📌 Proje 19: Otomatik Kayan İçerik (Element Scroller)

Haber sitelerindeki alt bant gibi, yazıların ekranın altından durmadan sola doğru akmasını sağlar. Fare üzerine gelince akış durur (mouseover), fare çekilince devam eder.

📌 Proje 20: Sürükle Bırak Çöp Kutusu Uygulaması (DOM Trash Bin)

Ekrandaki elementleri fareyle tutup ekran üzerinde herhangi bir yere sürüklememizi, çöp kutusu görselinin üzerine bıraktığımızda ise o elementi sayfadan tamamen silmemizi (Element.remove()) sağlar.

📌 Proje 21: Adam Asmaca (Hangman Game)

Gizli kelime kadar ekrana alt çizgi koyar. Alfabedeki harf butonlarına tıklandıkça harf kontrolü yapılır. Bilemedikçe adam asılma aşamaları görsel olarak güncellenir.

📌 Proje 22: Pop-up Modal Pencere Görünümü

Sayfanın ortasında açılan, arkadaki her şeyi karartan modern uyarı pencereleridir. Kapatma butonuna basıldığında CSS sınıfları kaldırılarak gizlenir.

📌 Proje 23: Resim Galerisi Slider (Image Carousel)

İleri ve geri ok butonlarına basıldığında resimlerin sırayla değiştiği, altında hangi resimde olduğumuzu gösteren küçük noktaların (indikatörler) yandığı slayt gösterisidir.

📌 Proje 24: Yıldız Değerlendirme Sistemi (Star Rating)

Ürün yorumlarında olan 5 yıldızlı sistemdir. Fareyle 3. yıldızın üzerine gelirseniz ilk 3 yıldız parlar (mouseover), tıkladığınızda o puan sabitlenir (click).

📌 Proje 25: Uçak Bombardıman Oyunu (Plane Bomber)

Ekranın üstünde sağa sola giden bir uçaktan boşluk tuşuyla bombalar bırakarak yerdeki hedefleri vurmaya çalıştığınız hareketli bir DOM oyunudur.

📌 Proje 26: Kutu Kovalama Oyunu (Chaser Box Game)

Oyun alanı içinde sürekli kaçan akıllı bir hedef kutu vardır. Kullanıcı kendi kutusunu yön tuşlarıyla hareket ettirerek kaçan kutuyu yakalamaya çalışır. Her yakalamada skor artar ve hedef başka yere ışınlanır.

📌 Proje 27: Bilgi İpucu Kutusu (Tooltip)

Bir kelimenin üzerine fareyle gelindiğinde, farenin hemen yanında siyah küçük bir açıklama balonu açılır. Farenin koordinatları (event.pageX, event.pageY) alınarak konumlandırılır.

📌 Proje 28: E-posta Ayıklayıcı (Email Extractor)

Kullanıcı karmaşık, upuzun bir yazı yapıştırır. JavaScript içerideki düzenli ifadeleri (RegEx) tarayarak sadece e-posta adreslerini cımbızla çeker gibi ayıklar ve listeler. Çift kayıtları eler.

📌 Proje 29: JavaScript Hesap Makinesi (Calculator)

Ekrandaki rakam ve işlem butonlarına basılarak matematiksel işlemler yapılan bir uygulamadır. Arka planda eval() metodu kullanılarak formüller sonuçlandırılır.

📌 Proje 30: Dinamik Alışveriş Listesi (Shopping List)

Listeye yeni eleman ekleme, eklenen elemanın yanındaki "Sil" butonuna basınca uçurma ve elemanın üzerine çift tıklayınca anında düzenlenebilir yazı kutusuna dönüştürme (contenteditable) özelliklerine sahiptir.

📌 Proje 31: Sahte Resim Linki Üretici (Placeholder Image Generator)

Kullanıcı genişlik, yükseklik ve renk seçer. JavaScript ona test çalışmalarında kullanabileceği dinamik bir resim url adresi üretip ekrana basar.

📌 Proje 32: Form Doğrulama Örneği (Form Validation)

Kullanıcı kayıt olurken şifrenin uzunluğunu, e-postada "@" işareti olup olmadığını denetler. Eğer kurallara uymuyorsa formu göndermez ve kutuların altına kırmızı uyarılar yazar.

📌 Proje 33: Çerez Test Makinesi (Cookie Tester)

Kullanıcının tarayıcısına küçük bilgi kırıntıları (çerez/cookie) bırakmasını, bu çerezlerin son kullanma tarihlerini ayarlamasını ve silmesini sağlayan yönetim panelidir.

📌 Proje 34: Sayfa Dinamosu (DOMinator)

Kullanıcının açılır menüden (Select Box) seçim yaparak sayfaya anında yeni butonlar, yeni inputlar ekleyebildiği veya silebildiği, DOM sınırlarını zorlayan bir yönetim aracıdır.

📌 Proje 35: Google E-Tablo Verisini CSV Dosyasına Çevirme (CSV Maker)

Tablo halindeki verileri virgüllerle ayrılmış metin dosyası biçimine (.csv) dönüştürüp kullanıcının bilgisayarına dosya olarak indirmesini sağlayan veri transfer projesidir.

📌 Proje 36: Flappy Bird Benzeri Kuş Uçurma Oyunu (Flying Bird)

Boşluk tuşuna bastıkça yukarı zıplayan, basmayınca yerçekimiyle düşen bir kuşun, karşıdan gelen engellerin arasından geçmesini sağlayan çarpışma algılamalı tam fonksiyonel oyundur.

📌 Proje 37: Sayı Tahmin Oyunu (Number Guessing Game)

Bilgisayar 1-100 arası sayı tutar. Kullanıcı tahmin eder. "Yukarı", "Aşağı" yönlendirmeleriyle haklar azalarak sayı bulunmaya çalışılır.

📌 Proje 38: Uzay İstilacıları Oyunu (Alien Invader)

Klasik atari oyunudur. Ekranın üstündeki uzaylı ordusu sağa sola giderek aşağı yaklaşır. Aşağıdaki oyuncu sağa sola kaçarak uzaylıları mermilerle vurup yok eder.

📌 Proje 39: Kart Savaşı Oyunu (Card War Game)

52'lik deste karıştırılır ve iki oyuncuya dağıtılır. Butona basıldığında kartlar açılır, değeri büyük olan kartı alan o elin puanını kapar. Deste bitene kadar savaş sürer.

📌 Proje 40: YouTube API Bağlantılı Video Arama Sistemi

Kullanıcı arama kutusuna bir kelime yazar. Kod, YouTube sunucularına arka planda bağlanarak gelen video sonuçlarını başlıkları ve küçük resimleriyle sayfaya listeler. Sayfalama (Sonraki sayfa) mantığını içerir.

📌 Proje 41: Tuğla Kırma Oyunu (Brick Breaker)

Alttaki tahtayı fareyle sağa sola oynatarak zıplayan topu düşürmemeye çalışırsınız. Top yukarıdaki tuğlalara çarptıkça tuğlalar kırılır ve yok olur.

📌 Proje 42: Nesne Yönelimli Programlama (OOP) Yapıları ve Alışveriş Sepeti

JavaScript'teki class (Sınıf) yapılarını öğreniriz. Ürünler ve Alışveriş sepeti birer yazılımsal nesne olarak kurgulanır. Sepete ekle, adeti arttır, KDV hesapla gibi işler fonksiyonel objeler üzerinden yürütülür.

📌 Proje 43: Var mısın Yok musun Oyunu (Deal Game)

Ekranda 24 adet kapalı kutu oluşturulur. Kullanıcı bir kutu seçer. Ardından diğer kutuları tek tek açarak büyük ödülleri eler. Belirli turlarda banka kullanıcıya teklif sunar ("Kabul" veya "Reddet").

📌 Proje 44: Sonsuz Kaydırma Sayfası (Endless Scroll / JSON Fetch)

Kullanıcı fare tekerleğiyle sayfanın en altına geldiğinde, JavaScript bunu fark eder ve internetten yeni veri (örneğin yeni Instagram fotoğrafları gibi) çekip sayfanın altına otomatik ekler. Sayfa hiç bitmez.

📌 Proje 45: Blackjack / 21 Oyunu

Yazılımsal olarak iskambil kartları oluşturulur, karıştırılır (Shuffle). Oyuncu "Kart Al" veya "Kal" der. Kurpiyerin elindeki sayılarla karşılaştırılıp 21'e en yakın olan veya tam 21 yapan kazanır, bahisler ödenir.


Bölüm 46: İleri Düzey JavaScript Ödevleri ve Teknik Konu Açıklamaları

Bu kısım mülakatlarda (iş görüşmelerinde) karşımıza çıkan kıdemli yazılımcı konularını içerir.

⚡ Nesneyi Derinlemesine Klonlamak (Deep Cloning An Object)

JavaScript'te bir objeyi direkt başka bir değişkene eşitlerseniz, sadece adresini kopyalarsınız. Birinde yapılan değişiklik diğerini de bozar (Shallow Copy). deepClone fonksiyonu hafızada tamamen bağımsız yeni bir kopya oluşturur.

function deepClone(obj) {
    return JSON.parse(JSON.stringify(obj));
}
let orijinal = { ad: "Can", detay: { yas: 30 } };
let kopya = deepClone(orijinal);
kopya.detay.yas = 31; // Orijinaldeki yaş hala 30 kalır, bozulmaz.

⚡ Özel Promise Yapısı İnşa Etmek (Implementing a Simple Promise)

Asenkron işlemlerin (zaman alan işlerin) bittiğinde bize olumlu (resolve) veya olumsuz (reject) cevap dönmesini sağlayan, zincirleme .then() komutlarıyla birbirine bağlanan JavaScript söz verme motorunun ham halini sıfırdan kodlamaktır.

⚡ Geciktirme Fonksiyonu (Debouncing Function)

Kullanıcı klavyede yazı yazarken, o her harfe bastığında internette arama yaparsak sistem çöker. Debounce, kullanıcının tuşlara basmayı tamamen bitirmesini bekler (Örneğin yarım saniye duraksadıysa), ardından tek bir defa işlem yapar.

function debounce(fn, beklemeSuresi) {
    let zamanlayici;
    return function(...args) {
        clearTimeout(zamanlayici);
        zamanlayici = setTimeout(() => fn.apply(this, args), beklemeSuresi);
    };
}

⚡ Hız Sınırlayıcı Fonksiyon (Throttle Function)

Özellikle sayfa aşağı kaydırılırken (scroll olayı) tarayıcı salaniyede yüzlerce kez kod çalıştırır. Throttle, bu tetiklenme ne kadar çok olursa olsun, fonksiyonun sadece belirlenen süre aralığında (örneğin 200 milisaniyede bir) en fazla bir kez çalışmasını zorunlu kılar. Tarayıcıyı rahatlatır.

⚡ Basit Yönlendirici Sistemi (Simple Router)

Sayfa yenilenmeden, sadece üstteki URL kısmındaki diyez (#about, #contact) işaretine bakarak ekranın ortasındaki içeriği dinamik değiştiren, dış kütüphanesiz Single Page Application (SPA) alt yapısı kurmaktır.

⚡ Resimleri Tembelce Yükleme (Image Lazy Loader)

Bir web sayfasında 100 tane büyük resim varsa, site ilk açıldığında hepsi yüklenirse internet çöker. Bu kod, kullanıcının ekranı aşağı kaydırıp ilgili resmin kutusunun yanına gelip gelmediğini kontrol eder (getBoundingClientRect). Resim ekrana girdiği an gerçek resmi yükler.

⚡ Karmaşık İç İçe Dizileri Düzleştirmek (Flatten Nested Array)

İç içe geçmiş kutular gibi duran dizileri tek katmanlı hale getirme işlemidir. Rekürsiyon ve .reduce() yöntemi kullanılır.

// Örnek girdi: [1, [2, [3, 4]], 5]
// Çıktı: [1, 2, 3, 4, 5]
function duzlestir(dizi) {
    return dizi.reduce((acc, eleman) => 
        Array.isArray(eleman) ? acc.concat(duzlestir(eleman)) : acc.concat(eleman), []);
}
console.log(duzlestir([1, [2, 3], 4])); // [1, 2, 3, 4]

⚡ Fonksiyon Parçalama / Körileme (Currying Function)

Çok parametreli bir fonksiyonu, her seferinde tek bir parametre alan ve geriye yeni bir fonksiyon döndüren zincirleme bir yapıya dönüştürme sanatıdır. Fonksiyonel programlamanın temel ileri düzey tekniğidir.

// Normal: topla(2, 3) yerine Currying: topla(2)(3)
const topla = a => b => a + b;
console.log(topla(5)(10)); // 15 sonucunu verir.

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

Müfredatta yer alan 45 projenin tamamını tek bir seferde tek bir mesaja sığdırmak, yapay zeka hafıza ve karakter sınırları (output token limitleri) nedeniyle teknik olarak imkansızdır. Kodların kesilmemesi, eksik kalmaması ve tarayıcınızda doğrudan çift tıklayarak sorunsuz çalışabilmesi için projeleri bölüm bölüm ve sırayla inşa etmemiz gerekiyor.

İlk aşamada, kursun temelini ve ilk dinamik uygulamalarını oluşturan İlk 5 Projeyi tüm HTML, CSS ve JavaScript kodları tek bir dosyada olacak şekilde (kopyala-yapıştır yapıp çalıştırabileceğiniz biçimde) aşağıda hazırladım.


📌 Proje 1: Bahşiş Hesaplayıcı (Tip Calculator)

Dosya Adı: 1-bahsis-hesaplayici.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bahşiş Hesaplayıcı</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: #f4f7f6;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .container {
            background-color: white;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 8px 24px rgba(0,0,0,0.1);
            width: 350px;
        }
        h2 { text-align: center; color: #2c3e50; margin-top: 0; }
        .form-group { margin-bottom: 20px; }
        label { display: block; margin-bottom: 8px; color: #7f8c8d; font-weight: bold; }
        input, select {
            width: 100%;
            padding: 10px;
            border: 2px solid #bdc3c7;
            border-radius: 6px;
            box-sizing: border-box;
            font-size: 16px;
        }
        button {
            width: 100%;
            background-color: #2ecc71;
            color: white;
            border: none;
            padding: 12px;
            border-radius: 6px;
            font-size: 18px;
            cursor: pointer;
            transition: background 0.2s;
        }
        button:hover { background-color: #27ae60; }
        .result {
            margin-top: 20px;
            padding: 15px;
            background-color: #e8f8f5;
            border-radius: 6px;
            text-align: center;
            font-size: 18px;
            font-weight: bold;
            color: #16a085;
            display: none;
        }
    </style>
</head>
<body>

<div class="container">
    <h2>Bahşiş Hesapla</h2>
    <div class="form-group">
        <label for="bill">Hesap Tutarı (TL)</label>
        <input type="number" id="bill" placeholder="Örn: 250">
    </div>
    <div class="form-group">
        <label for="service">Hizmet Kalitesi</label>
        <select id="service">
            <option value="30">Harika (%30)</option>
            <option value="20">İyi (%20)</option>
            <option value="15" selected>Standart (%15)</option>
            <option value="10">Kötü (%10)</option>
        </select>
    </div>
    <button id="calculate">Hesapla</button>
    <div class="result" id="result"></div>
</div>

<script>
    document.getElementById('calculate').addEventListener('click', function() {
        const bill = parseFloat(document.getElementById('bill').value);
        const service = parseFloat(document.getElementById('service').value);
        const resultDiv = document.getElementById('result');

        if (isNaN(bill) || bill <= 0) {
            alert('Lütfen geçerli bir hesap tutarı girin.');
            return;
        }

        const tipAmount = bill * (service / 100);
        const totalAmount = bill + tipAmount;

        resultDiv.innerHTML = `Bahşiş: ${tipAmount.toFixed(2)} TL<br>Toplam: ${totalAmount.toFixed(2)} TL`;
        resultDiv.style.display = 'block';
    });
</script>
</body>
</html>

📌 Proje 2: Zamana Göre Değişen Dinamik Karşılayıcı

Dosya Adı: 2-dinamik-karsilayici.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dinamik Karşılama</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            margin: 0;
            height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            transition: all 1s ease;
            color: white;
            text-shadow: 0 2px 4px rgba(0,0,0,0.5);
        }
        .clock { font-size: 4rem; font-weight: bold; margin-bottom: 10px; }
        .greeting { font-size: 2.5rem; }
    </style>
</head>
<body>

<div class="clock" id="clock">00:00:00</div>
<div class="greeting" id="greeting">Yükleniyor...</div>

<script>
    function updatePage() {
        const now = new Date();
        const hours = now.getHours();
        const minutes = String(now.getMinutes()).padStart(2, '0');
        const seconds = String(now.getSeconds()).padStart(2, '0');
        
        document.getElementById('clock').textContent = `${String(hours).padStart(2, '0')}:${minutes}:${seconds}`;
        
        const greetingDiv = document.getElementById('greeting');
        
        if (hours >= 5 && hours < 12) {
            greetingDiv.textContent = "Günaydın!";
            document.body.style.backgroundColor = "#f39c12"; // Gün doğumu turuncusu
        } else if (hours >= 12 && hours < 18) {
            greetingDiv.textContent = "Tünaydın!";
            document.body.style.backgroundColor = "#3498db"; // Gündüz mavisi
        } else if (hours >= 18 && hours < 22) {
            greetingDiv.textContent = "İyi Akşamlar!";
            document.body.style.backgroundColor = "#e67e22"; // Gün batımı
        } else {
            greetingDiv.textContent = "İyi Geceler!";
            document.body.style.backgroundColor = "#2c3e50"; // Gece karanlığı
        }
    }

    setInterval(updatePage, 1000);
    updatePage(); // İlk açılışta hemen çalışsın
</script>
</body>
</html>

📌 Proje 3: Yazı Tura Uygulaması (Coin Toss)

Dosya Adı: 3-yazi-tura.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Yazı Tura Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background: #f0f2f5;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }
        .coin {
            width: 150px;
            height: 150px;
            background-color: #f1c40f;
            border: 8px solid #f39c12;
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 24px;
            font-weight: bold;
            color: #7f8c8d;
            box-shadow: 0 6px 12px rgba(0,0,0,0.15);
            transition: transform 0.5s ease;
        }
        .spin { transform: rotateY(720deg); }
        button {
            margin-top: 30px;
            padding: 12px 30px;
            font-size: 18px;
            background-color: #9b59b6;
            color: white;
            border: none;
            border-radius: 25px;
            cursor: pointer;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
        button:hover { background-color: #8e44ad; }
        .stats { margin-top: 20px; font-size: 18px; color: #34495e; }
    </style>
</head>
<body>

<div class="coin" id="coin">?</div>
<button id="flipBtn">Parayı At!</button>
<div class="stats" id="stats">Yazı: 0 | Tura: 0</div>

<script>
    let heads = 0; // Yazı
    let tails = 0; // Tura

    document.getElementById('flipBtn').addEventListener('click', function() {
        const coin = document.getElementById('coin');
        coin.classList.add('spin');
        coin.textContent = "...";

        setTimeout(() => {
            coin.classList.remove('spin');
            const isHeads = Math.random() < 0.5;
            
            if (isHeads) {
                coin.textContent = "YAZI";
                coin.style.backgroundColor = "#f1c40f";
                heads++;
            } else {
                coin.textContent = "TURA";
                coin.style.backgroundColor = "#e67e22";
                tails++;
            }
            document.getElementById('stats').textContent = `Yazı: ${heads} | Tura: ${tails}`;
        }, 500);
    });
</script>
</body>
</html>

📌 Proje 4: Taş Kağıt Makas Oyunu (Rock Paper Scissors)

Dosya Adı: 4-tas-kagit-makas.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Taş Kağıt Makas</title>
    <style>
        body {
            font-family: sans-serif;
            text-align: center;
            background: #e1e8ed;
            margin: 0;
            padding: 20px;
        }
        .choices { display: flex; justify-content: center; gap: 20px; margin: 30px 0; }
        .btn {
            font-size: 2rem;
            padding: 15px 30px;
            cursor: pointer;
            border: none;
            border-radius: 10px;
            background: white;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            transition: transform 0.1s;
        }
        .btn:hover { transform: scale(1.1); }
        .score-board { font-size: 1.5rem; margin: 20px; color: #2c3e50; }
        .message { font-size: 1.3rem; font-weight: bold; color: #16a085; min-height: 30px; }
    </style>
</head>
<body>

    <h1>Taş - Kağıt - Makas</h1>
    <div class="score-board">
        Oyuncu: <span id="player-score">0</span> | Bilgisayar: <span id="comp-score">0</span>
    </div>
    
    <div class="choices">
        <button class="btn" onclick="play('Taş')">✊</button>
        <button class="btn" onclick="play('Kağıt')">✋</button>
        <button class="btn" onclick="play('Makas')">✌️</button>
    </div>

    <div class="message" id="message">Seçimini yap ve oyuna başla!</div>

<script>
    let pScore = 0;
    let cScore = 0;
    const options = ["Taş", "Kağıt", "Makas"];

    function play(playerChoice) {
        const compChoice = options[Math.floor(Math.random() * 3)];
        let msg = `Sen: ${playerChoice} vs Bilgisayar: ${compChoice}. `;

        if (playerChoice === compChoice) {
            msg += "Berabere!";
        } else if (
            (playerChoice === "Taş" && compChoice === "Makas") ||
            (playerChoice === "Kağıt" && compChoice === "Taş") ||
            (playerChoice === "Makas" && compChoice === "Kağıt")
        ) {
            msg += "Kazandın! 🎉";
            pScore++;
        } else {
            msg += "Kaybettin! 😢";
            cScore++;
        }

        document.getElementById('player-score').textContent = pScore;
        document.getElementById('comp-score').textContent = cScore;
        document.getElementById('message').textContent = msg;
    }
</script>
</body>
</html>

📌 Proje 5: Gelişmiş Zar Oyunu (Ultimate Dice Game)

Dosya Adı: 5-gelismis-zar-oyunu.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gelişmiş Zar Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background: #27ae60;
            color: white;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }
        .board { display: flex; gap: 40px; margin: 30px; }
        .dice-container { text-align: center; }
        .dice {
            width: 100px;
            height: 100px;
            background: white;
            border-radius: 15px;
            box-shadow: 0 10px 20px rgba(0,0,0,0.3);
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            grid-template-rows: repeat(3, 1fr);
            padding: 10px;
            box-sizing: border-box;
        }
        .dot {
            background: #333;
            border-radius: 50%;
            width: 16px;
            height: 16px;
            margin: auto;
            visibility: hidden;
        }
        /* Zar Nokta Pozisyon Matrisi */
        .visible { visibility: visible; }
        button {
            padding: 12px 40px;
            font-size: 20px;
            font-weight: bold;
            color: #27ae60;
            background: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            box-shadow: 0 5px 15px rgba(0,0,0,0.2);
        }
        button:hover { background: #f0f0f0; }
        #result-text { font-size: 24px; font-weight: bold; margin-top: 20px; min-height: 35px;}
    </style>
</head>
<body>

    <h1>Zar Atma Düellosu</h1>
    <div class="board">
        <div class="dice-container">
            <h3>Oyuncu 1</h3>
            <div class="dice" id="dice1"></div>
        </div>
        <div class="dice-container">
            <h3>Oyuncu 2</h3>
            <div class="dice" id="dice2"></div>
        </div>
    </div>
    <button id="rollBtn">Zarları At</button>
    <div id="result-text"></div>

<script>
    // 1-6 arasındaki sayıların 3x3 grid üzerindeki nokta indeks haritası
    const diceMaps = {
        1:,
        2:,
        3:,
        4:,
        5:,
        6: [0, 2, 3, 5, 6, 8]
    };

    function buildDice(diceElement, value) {
        diceElement.innerHTML = ''; // Temizle
        for (let i = 0; i < 9; i++) {
            const dot = document.createElement('div');
            dot.className = 'dot';
            if (diceMaps[value].includes(i)) {
                dot.classList.add('visible');
            }
            diceElement.appendChild(dot);
        }
    }

    // İlk kurulum boş zarlar
    buildDice(document.getElementById('dice1'), 1);
    buildDice(document.getElementById('dice2'), 1);

    document.getElementById('rollBtn').addEventListener('click', function() {
        const val1 = Math.floor(Math.random() * 6) + 1;
        const val2 = Math.floor(Math.random() * 6) + 1;

        buildDice(document.getElementById('dice1'), val1);
        buildDice(document.getElementById('dice2'), val2);

        const resultText = document.getElementById('result-text');
        if (val1 > val2) {
            resultText.textContent = "🏆 Oyuncu 1 Kazandı!";
        } else if (val2 > val1) {
            resultText.textContent = "Oyuncu 2 Kazandı! 🏆";
        } else {
            resultText.textContent = "🤝 Berabere!";
        }
    });
</script>
</body>
</html>

Müfredat listemizde sıradaki 6. ve 10. projeler arasındaki uygulamaların tüm HTML, CSS ve JavaScript kod bloklarını, tarayıcınızda doğrudan çalıştırabileceğiniz şekilde tek bir dosya formatında hazırladım.


📌 Proje 6: Ses Çalar Uygulaması (Play Audio Files)

Bu uygulama, sayfada dinamik olarak ses butonları oluşturur ve tarayıcının yerleşik ses sentezleyicisini (SpeechSynthesis) kullanarak ek bir .mp3 dosyasına ihtiyaç duymadan ses efektleri üretir.
Dosya Adı: 6-ses-calar.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Ses Çalar</title>
    <style>
        body {
            font-family: 'Segoe UI', sans-serif;
            background: #1e1e24;
            color: white;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }
        h1 { margin-bottom: 30px; color: #4a90e2; }
        .buttons-container {
            display: flex;
            gap: 15px;
            flex-wrap: wrap;
            justify-content: center;
            max-width: 600px;
        }
        .sound-btn {
            background: #2a2b3d;
            border: 2px solid #4a90e2;
            color: white;
            padding: 20px 40px;
            font-size: 18px;
            font-weight: bold;
            border-radius: 10px;
            cursor: pointer;
            transition: all 0.2s ease;
            box-shadow: 0 4px 15px rgba(0,0,0,0.2);
        }
        .sound-btn:hover {
            background: #4a90e2;
            transform: translateY(-3px);
            box-shadow: 0 6px 20px rgba(74, 144, 226, 0.4);
        }
        .sound-btn:active { transform: translateY(0); }
    </style>
</head>
<body>

    <h1>Müzik & Efekt Kutusu</h1>
    <div class="buttons-container" id="btnContainer"></div>

<script>
    // Dinamik butonlar için ses isimleri listesi
    const sounds = ["Alkış", "Hata", "Lazer", "Başarı", "Davul"];
    const container = document.getElementById('btnContainer');

    sounds.forEach(sound => {
        const btn = document.createElement('button');
        btn.className = 'sound-btn';
        btn.innerText = sound;

        btn.addEventListener('click', () => {
            playSyntheticSound(sound);
        });

        container.appendChild(btn);
    });

    // Harici mp3 bağımlılığı olmadan ses üretme fonksiyonu
    function playSyntheticSound(type) {
        const synth = window.speechSynthesis;
        if (!synth) {
            alert("Tarayıcınız ses sentezini desteklemiyor.");
            return;
        }
        
        let word = type;
        if(type === "Alkış") word = "Bravo! Muhteşem!";
        if(type === "Hata") word = "Dikkat! Hata oluştu.";
        if(type === "Lazer") word = "Piuw! Piuw!";
        if(type === "Başarı") word = "Tebrikler! Kazandınız.";
        if(type === "Davul") word = "Güm! Güm! Taka tak!";

        const utterance = new SpeechSynthesisUtterance(word);
        utterance.lang = 'tr-TR';
        utterance.rate = 1.2; 
        synth.speak(utterance);
    }
</script>
</body>
</html>

📌 Proje 7: Dinamik Klavye Hız Testi (Dynamic Typing Test)

Ekrana gelen rastgele cümleleri kullanıcının ne kadar hızlı ve doğru yazdığını ölçer. Dakika başına kelime (WPM) ve doğruluk oranı hesaplar.
Dosya Adı: 7-klavye-hiz-testi.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Klavye Hız Testi</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background: #f4f6f9;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .card {
            background: white;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.05);
            width: 550px;
            box-sizing: border-box;
        }
        .quote {
            background: #eef2f7;
            padding: 15px;
            border-radius: 6px;
            font-size: 18px;
            margin-bottom: 20px;
            line-height: 1.5;
            color: #333;
            user-select: none;
        }
        textarea {
            width: 100%;
            height: 100px;
            padding: 10px;
            font-size: 16px;
            border: 2px solid #ddd;
            border-radius: 6px;
            resize: none;
            box-sizing: border-box;
        }
        textarea:focus { border-color: #3498db; outline: none; }
        .score-board {
            display: flex;
            justify-content: space-between;
            margin-top: 20px;
            font-size: 16px;
            font-weight: bold;
            color: #555;
        }
        button {
            margin-top: 15px;
            width: 100%;
            padding: 12px;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 16px;
            cursor: pointer;
        }
        button:hover { background: #2980b9; }
    </style>
</head>
<body>

<div class="card">
    <h2>Klavye Hız Testi</h2>
    <div class="quote" id="quoteTarget">Yükleniyor...</div>
    <textarea id="typeInput" placeholder="Yazmaya başladığınızda süre tetiklenecektir..." disabled></textarea>
    <div class="score-board">
        <div>Süre: <span id="timer">0</span>s</div>
        <div>WPM (Hız): <span id="wpm">0</span></div>
        <div>Doğruluk: <span id="accuracy">0%</span></div>
    </div>
    <button id="startBtn">Testi Başlat</button>
</div>

<script>
    const quotes = [
        "JavaScript web sayfalarını dinamik hale getiren güçlü bir dildir.",
        "Hızlı yazmak sadece parmakların değil düzenli pratiğin bir sonucudur.",
        "Kod yazarken hata yapmaktan korkmayın çünkü en büyük öğretmen hatalardır.",
        "Teknoloji her gün gelişiyor ve bizim de kendimizi güncellememiz gerekiyor."
    ];

    let startTime, timerInterval;
    let isRunning = false;
    let selectedQuote = "";

    const quoteTarget = document.getElementById('quoteTarget');
    const typeInput = document.getElementById('typeInput');
    const timerText = document.getElementById('timer');
    const wpmText = document.getElementById('wpm');
    const accText = document.getElementById('accuracy');
    const startBtn = document.getElementById('startBtn');

    startBtn.addEventListener('click', startTest);
    typeInput.addEventListener('input', checkTyping);

    function startTest() {
        isRunning = true;
        typeInput.disabled = false;
        typeInput.value = "";
        typeInput.focus();
        
        selectedQuote = quotes[Math.floor(Math.random() * quotes.length)];
        quoteTarget.textContent = selectedQuote;
        
        startTime = new Date();
        clearInterval(timerInterval);
        timerInterval = setInterval(updateTimer, 1000);
        startBtn.textContent = "Yeniden Başlat";
    }

    function updateTimer() {
        const timePassed = Math.floor((new Date() - startTime) / 1000);
        timerText.textContent = timePassed;
    }

    function checkTyping() {
        const currentInput = typeInput.value;
        
        // Doğruluk hesaplama
        let correctChars = 0;
        for (let i = 0; i < currentInput.length; i++) {
            if (currentInput[i] === selectedQuote[i]) correctChars++;
        }
        const acc = currentInput.length > 0 ? Math.floor((correctChars / currentInput.length) * 100) : 0;
        accText.textContent = acc + "%";

        // Hız (WPM) hesaplama
        const timePassedMin = (new Date() - startTime) / 60000;
        const wordsCount = currentInput.split(' ').length;
        if (timePassedMin > 0) {
            wpmText.textContent = Math.floor(wordsCount / timePassedMin);
        }

        // Test bitti mi?
        if (currentInput === selectedQuote) {
            clearInterval(timerInterval);
            typeInput.disabled = true;
            alert("Tebrikler! Testi başarıyla tamamladınız.");
        }
    }
</script>
</body>
</html>

📌 Proje 8: Nesne Hareket Ettirici (Functionator / DOM Mover)

Klavyedeki yön tuşlarıyla harita üzerindeki mavi bloğu sınırların dışına çıkmadan hareket ettirmenizi sağlar. Hareket geçmişini sağ taraftaki listeye kaydeder.
Dosya Adı: 8-dom-mover.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DOM Mover</title>
    <style>
        body {
            font-family: sans-serif;
            background: #2c3e50;
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            gap: 30px;
        }
        .game-area {
            width: 400px;
            height: 400px;
            background: #34495e;
            border: 4px solid #1abc9c;
            position: relative;
            border-radius: 8px;
        }
        .player {
            width: 40px;
            height: 40px;
            background: #3498db;
            position: absolute;
            top: 180px;
            left: 180px;
            border-radius: 6px;
            transition: all 0.1s ease;
            box-shadow: 0 0 10px #3498db;
        }
        .log-panel {
            width: 200px;
            height: 400px;
            background: rgba(255,255,255,0.1);
            border-radius: 8px;
            padding: 15px;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
        }
        .log-list {
            overflow-y: auto;
            flex-grow: 1;
            padding-left: 20px;
            margin: 0;
            font-size: 14px;
            color: #bdc3c7;
        }
    </style>
</head>
<body>

    <div class="game-area">
        <div class="player" id="player"></div>
    </div>

    <div class="log-panel">
        <h3>Aksiyon Listesi</h3>
        <ul class="log-list" id="logList"></ul>
    </div>

<script>
    const player = document.getElementById('player');
    const logList = document.getElementById('logList');
    
    // Başlangıç pozisyon değerleri
    let posX = 180;
    let posY = 180;
    const step = 20; // Her adım mesafesi
    const maxBound = 360; // 400px alan - 40px oyuncu genişliği

    window.addEventListener('keydown', (e) => {
        let action = "";
        
        switch(e.key) {
            case "ArrowUp":
            case "w":
                if (posY >= step) { posY -= step; action = "Yukarı"; }
                break;
            case "ArrowDown":
            case "s":
                if (posY <= maxBound - step) { posY += step; action = "Aşağı"; }
                break;
            case "ArrowLeft":
            case "a":
                if (posX >= step) { posX -= step; action = "Sola"; }
                break;
            case "ArrowRight":
            case "d":
                if (posX <= maxBound - step) { posX += step; action = "Sağa"; }
                break;
        }

        if (action !== "") {
            // DOM Pozisyon Güncelleme
            player.style.top = posY + "px";
            player.style.left = posX + "px";

            // Listeye Eylem Ekleme
            const li = document.createElement('li');
            li.textContent = `${action} (${posX}, ${posY})`;
            logList.appendChild(li);
            logList.scrollTop = logList.scrollHeight; // Otomatik aşağı kaydır
        }
    });
</script>
</body>
</html>

📌 Proje 9: Sihirli 8 Top (Magic Eight Ball)

Sorulan sorulara rastgele gizemli cevaplar veren eğlenceli bir şans uygulamasıdır. İçerideki küre animasyonlu bir şekilde sallanarak cevabı yansıtır.
Dosya Adı: 9-sihirli-8-top.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sihirli 8 Top</title>
    <style>
        body {
            font-family: sans-serif;
            background: #111;
            color: white;
            text-align: center;
            padding-top: 50px;
        }
        .ball {
            width: 250px;
            height: 250px;
            background: radial-gradient(circle at 75px 75px, #333, #000);
            border-radius: 50%;
            margin: 30px auto;
            display: flex;
            justify-content: center;
            align-items: center;
            box-shadow: 0 15px 30px rgba(0,0,0,0.5);
            position: relative;
        }
        .inner-circle {
            width: 110px;
            height: 110px;
            background: #fff;
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            color: black;
            font-weight: bold;
            font-size: 14px;
            padding: 10px;
            box-sizing: border-box;
            text-align: center;
            transition: all 0.3s;
        }
        input {
            padding: 10px;
            width: 280px;
            font-size: 16px;
            border-radius: 5px;
            border: none;
            margin-right: 10px;
        }
        button {
            padding: 10px 20px;
            font-size: 16px;
            background: #8e44ad;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
        button:hover { background: #9b59b6; }
        
        /* Sallanma Animasyonu */
        @keyframes shake {
            0% { transform: translate(1px, 1px) rotate(0deg); }
            10% { transform: translate(-1px, -2px) rotate(-1deg); }
            30% { transform: translate(3px, 2px) rotate(0deg); }
            50% { transform: translate(-1px, 2px) rotate(1deg); }
            70% { transform: translate(2px, 1px) rotate(0deg); }
            100% { transform: translate(1px, -2px) rotate(-1deg); }
        }
        .shake-animation { animation: shake 0.5s infinite; }
    </style>
</head>
<body>

    <h1>Sihirli 8 Topa Soru Sor</h1>
    <p>Geleceği merak ediyorsan sorunu yaz ve topu salla!</p>
    
    <input type="text" id="question" placeholder="Zengin olacak mıyım?">
    <button id="askBtn">Sor</button>

    <div class="ball" id="ball">
        <div class="inner-circle" id="answer">8</div>
    </div>

<script>
    const answers = [
        "Kesinlikle evet", "Şüphesiz ki öyle", "Gördüğüm kadarıyla evet",
        "Buna güvenebilirsin", "Bana göre hava hoş", "Sonra tekrar sor",
        "Şimdi söylemesem iyi olur", "Tahmin edemiyorum", "Buna odaklan ve tekrar sor",
        "Kaynağım hayır diyor", "Görünüşe göre pek iyi değil", "Çok şüpheli"
    ];

    const ball = document.getElementById('ball');
    const answerDiv = document.getElementById('answer');
    const questionInput = document.getElementById('question');
    const askBtn = document.getElementById('askBtn');

    askBtn.addEventListener('click', () => {
        if (questionInput.value.trim() === "") {
            alert("Lütfen önce bir soru yazın!");
            return;
        }

        // Sallanma efekti tetikleme
        ball.classList.add('shake-animation');
        answerDiv.style.opacity = "0";

        setTimeout(() => {
            ball.classList.remove('shake-animation');
            const randomAnswer = answers[Math.floor(Math.random() * answers.length)];
            answerDiv.textContent = randomAnswer;
            answerDiv.style.opacity = "1";
            answerDiv.style.fontSize = "12px";
        }, 1000);
    });
</script>
</body>
</html>

📌 Proje 10: JavaScript Akordeon Menü (Accordion Project)

Sıkça sorulan sorular ekranlarında kullanılan, açıldığında yumuşak geçişle paneli gösteren ve diğer panelleri otomatik kapatan modüler bir bileşendir.
Dosya Adı: 10-akordeon-menu.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Akordeon Menü</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background: #fafafa;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding-top: 50px;
        }
        .accordion-container { width: 500px; }
        .accordion-item {
            background: white;
            margin-bottom: 10px;
            border-radius: 6px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.05);
            overflow: hidden;
        }
        .accordion-header {
            background: #ffffff;
            padding: 15px 20px;
            font-weight: bold;
            color: #333;
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
            user-select: none;
            border-bottom: 1px solid #f0f0f0;
        }
        .accordion-header:hover { background: #fdfdfd; color: #3498db; }
        .accordion-content {
            padding: 0 20px;
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.3s ease-out, padding 0.3s ease-out;
            color: #666;
            line-height: 1.6;
            font-size: 15px;
        }
        .active .accordion-content {
            padding: 15px 20px;
        }
        .icon { transition: transform 0.3s; }
        .active .icon { transform: rotate(45deg); }
    </style>
</head>
<body>

<div class="accordion-container">
    <h2>Sıkça Sorulan Sorular</h2>
    
    <div class="accordion-item">
        <div class="accordion-header">JavaScript Nedir? <span class="icon">+</span></div>
        <div class="accordion-content">JavaScript, web sayfalarını dinamik ve etkileşimli hale getirmek için kullanılan modern ve çok yönlü bir programlama dilidir.</div>
    </div>

    <div class="accordion-item">
        <div class="accordion-header">DOM Nedir? <span class="icon">+</span></div>
        <div class="accordion-content">Document Object Model (DOM), tarayıcının HTML sayfasını bir ağaç yapısı olarak görmesini ve kodla müdahale edilmesini sağlayan yapıdır.</div>
    </div>

    <div class="accordion-item">
        <div class="accordion-header">Müfredat Hangi Seviyeye Uygundur? <span class="icon">+</span></div>
        <div class="accordion-content">Bu eğitim sıfırdan başlar ve sizi adım adım ileri düzey asenkron JavaScript ve oyun geliştirme seviyesine kadar taşır.</div>
    </div>
</div>

<script>
    const headers = document.querySelectorAll('.accordion-header');

    headers.forEach(header => {
        header.addEventListener('click', function() {
            const item = this.parentElement;
            const content = this.nextElementSibling;
            
            // Diğer tüm açık panelleri kapatma (Single Open kuralı)
            document.querySelectorAll('.accordion-item').forEach(el => {
                if (el !== item) {
                    el.classList.remove('active');
                    el.querySelector('.accordion-content').style.maxHeight = null;
                }
            });

            // Seçilen paneli açıp kapatma toggle işlemi
            item.classList.toggle('active');
            if (item.classList.contains('active')) {
                content.style.maxHeight = content.scrollHeight + "px";
            } else {
                content.style.maxHeight = null;
            }
        });
    });
</script>
</body>
</html>

Müfredat sırasındaki 11. ve 15. projeler arasındaki uygulamaların tüm HTML, CSS ve JavaScript kod bloklarını, tarayıcınızda doğrudan kopyalayıp çalıştırabileceğiniz şekilde tek bir dosya formatında hazırladım.


📌 Proje 11: Şifre Kırıcı Kasa Oyunu (Combination Guesser)

Bilgisayarın arka planda tuttuğu gizli 3 basamaklı şifreyi bulmaya çalışırsınız. Girdiğiniz sayılara göre her hanenin arkası renk değiştirerek size ipucu verir.
Dosya Adı: 11-sifre-krici-kasa.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Şifre Kırıcı Kasa Oyunu</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: #2c3e50;
            color: white;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }
        .kasa-box {
            background-color: #34495e;
            padding: 30px;
            border-radius: 15px;
            box-shadow: 0 10px 25px rgba(0,0,0,0.5);
            text-align: center;
            border: 4px solid #bdc3c7;
        }
        .inputs {
            display: flex;
            gap: 15px;
            justify-content: center;
            margin: 20px 0;
        }
        .digit-input {
            width: 60px;
            height: 60px;
            font-size: 28px;
            text-align: center;
            border-radius: 8px;
            border: 2px solid #7f8c8d;
            background-color: #ecf0f1;
            color: #2c3e50;
            font-weight: bold;
        }
        button {
            background-color: #e67e22;
            color: white;
            border: none;
            padding: 12px 30px;
            font-size: 18px;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            transition: 0.2s;
        }
        button:hover { background-color: #d35400; }
        .mesaj {
            margin-top: 20px;
            font-size: 18px;
            min-height: 25px;
            font-weight: bold;
        }
    </style>
</head>
<body>

<div class="kasa-box">
    <h2>🔒 Kasa Kilidi Şifre Kırıcı</h2>
    <p>3 basamaklı doğru şifreyi bulun (0-9 arası)</p>
    
    <div class="inputs">
        <input type="number" class="digit-input" min="0" max="9" maxlength="1">
        <input type="number" class="digit-input" min="0" max="9" maxlength="1">
        <input type="number" class="digit-input" min="0" max="9" maxlength="1">
    </div>

    <button id="kontrolBtn">Kilidi Açmayı Dene</button>
    <div class="mesaj" id="mesajBox"></div>
</div>

<script>
    // Bilgisayar 3 basamaklı rastgele şifre üretiyor
    const gizliSifre = [
        Math.floor(Math.random() * 10),
        Math.floor(Math.random() * 10),
        Math.floor(Math.random() * 10)
    ];
    let denemeSayisi = 0;

    const inputs = document.querySelectorAll('.digit-input');
    const mesajBox = document.getElementById('mesajBox');

    // Tek karakter sınırlaması ve otomatik odaklanma
    inputs.forEach((input, index) => {
        input.addEventListener('input', (e) => {
            if (input.value.length > 1) input.value = input.value.slice(0, 1);
            if (input.value !== "" && index < inputs.length - 1) {
                inputs[index + 1].focus();
            }
        });
    });

    document.getElementById('kontrolBtn').addEventListener('click', () => {
        let dogruHaneSayisi = 0;
        denemeSayisi++;

        inputs.forEach((input, index) => {
            const tahmin = parseInt(input.value);
            
            if (isNaN(tahmın)) {
                input.style.backgroundColor = "#ecf0f1";
                return;
            }

            if (tahmın === gizliSifre[index]) {
                input.style.backgroundColor = "#2ecc71"; // Doğru yer
                dogruHaneSayisi++;
            } else if (gizliSifre.includes(tahmın)) {
                input.style.backgroundColor = "#f1c40f"; // Şifrede var ama yanlış yerde
            } else {
                input.style.backgroundColor = "#e74c3c"; // Şifrede yok
            }
        });

        if (dogruHaneSayisi === 3) {
            mesajBox.innerHTML = `🎉 Tebrikler! Kasa açıldı. Şifre: ${gizliSifre.join('')} <br> ${denemeSayisi} denemede buldunuz.`;
            mesajBox.style.color = "#2ecc71";
            document.getElementById('kontrolBtn').disabled = true;
        } else {
            mesajBox.textContent = "Kilit açılmadı! Renk ipuçlarını takip edin.";
            mesajBox.style.color = "#f1c40f";
        }
    });
</script>
</body>
</html>

📌 Proje 12: Kelime Karıştırma Oyunu (Word Scramble)

Seçilen kelimenin harfleri havuzda karıştırılır. Kullanıcı kısıtlı süre bitmeden orijinal kelimeyi doğru tahmin etmeye çalışır.
Dosya Adı: 12-kelime-karistirma.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kelime Karıştırma Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background: #9b59b6;
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .oyun-kartı {
            background: white;
            color: #333;
            padding: 30px;
            border-radius: 12px;
            width: 400px;
            text-align: center;
            box-shadow: 0 8px 20px rgba(0,0,0,0.2);
        }
        .karisik-kelime {
            font-size: 32px;
            font-weight: bold;
            letter-spacing: 5px;
            margin: 20px 0;
            color: #8e44ad;
            text-transform: uppercase;
        }
        input {
            width: 80%;
            padding: 10px;
            font-size: 18px;
            border: 2px solid #ccc;
            border-radius: 6px;
            margin-bottom: 15px;
            text-align: center;
        }
        button {
            padding: 10px 20px;
            font-size: 16px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            margin: 5px;
            font-weight: bold;
        }
        .tahmin-btn { background: #2ecc71; color: white; }
        .yenile-btn { background: #e74c3c; color: white; }
        .bilgi { font-weight: bold; margin-top: 15px; font-size: 16px; }
    </style>
</head>
<body>

<div class="oyun-kartı">
    <h2>🔤 Kelime Karıştırma Oyunu</h2>
    <p>Harfleri karışık verilen kelimeyi tahmin edin:</p>
    <div class="karisik-kelime" id="scrambledWord">-----</div>
    
    <input type="text" id="userInput" placeholder="Tahmininizi yazın...">
    <div>
        <button class="tahmin-btn" id="checkBtn">Kontrol Et</button>
        <button class="yenile-btn" id="refreshBtn">Pas Geç</button>
    </div>
    <div class="bilgi" id="infoBox"></div>
</div>

<script>
    const kelimeler = ["javascript", "tarayici", "degisken", "fonksiyon", "dongu", "yazilim"];
    let secilenKelime = "";

    const scrambledWordDiv = document.getElementById('scrambledWord');
    const userInput = document.getElementById('userInput');
    const checkBtn = document.getElementById('checkBtn');
    const refreshBtn = document.getElementById('refreshBtn');
    const infoBox = document.getElementById('infoBox');

    function yeniKelimeSec() {
        userInput.value = "";
        infoBox.textContent = "";
        
        secilenKelime = kelimeler[Math.floor(Math.random() * kelimeler.length)];
        
        // Harfleri karıştırma algoritması
        let harfler = secilenKelime.split('');
        for (let i = harfler.length - 1; i > 0; i--) {
            let j = Math.floor(Math.random() * (i + 1));
            [harfler[i], harfler[j]] = [harfler[j], harfler[i]];
        }
        
        scrambledWordDiv.textContent = harfler.join('');
    }

    checkBtn.addEventListener('click', () => {
        const tahmin = userInput.value.trim().toLowerCase();
        if (tahmin === secilenKelime) {
            infoBox.textContent = "🎉 Doğru Tahmin! Harika.";
            infoBox.style.color = "#2ecc71";
            setTimeout(yeniKelimeSec, 1500);
        } else {
            infoBox.textContent = "❌ Yanlış Cevap, tekrar dene!";
            infoBox.style.color = "#e74c3c";
        }
    });

    refreshBtn.addEventListener('click', yeniKelimeSec);

    // Oyunu başlat
    yeniKelimeSec();
</script>
</body>
</html>

📌 Proje 13: Geri Sayım Sayacı (Countdown Timer)

Belirttiğiniz tarihe ne kadar süre kaldığını gün, saat, dakika ve saniye bazında canlı olarak güncelleyen profesyonel bir kronometre aracıdır.
Dosya Adı: 13-geri-sayim-sayaci.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Geri Sayım Sayacı</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background: linear-gradient(135deg, #111, #333);
            color: white;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }
        h1 { margin-bottom: 20px; letter-spacing: 2px; }
        .sayac-kutusu {
            display: flex;
            gap: 20px;
        }
        .zaman-bolumu {
            background: rgba(255,255,255,0.1);
            padding: 20px;
            border-radius: 10px;
            min-width: 80px;
            text-align: center;
            box-shadow: 0 5px 15px rgba(0,0,0,0.3);
        }
        .sayi {
            font-size: 40px;
            font-weight: bold;
            color: #f1c40f;
        }
        .etiket {
            font-size: 12px;
            text-transform: uppercase;
            color: #ccc;
            margin-top: 5px;
        }
    </style>
</head>
<body>

    <h1>Yeni Yıla Kalan Süre</h1>
    <div class="sayac-kutusu">
        <div class="zaman-bolumu">
            <div class="sayi" id="gun">00</div>
            <div class="etiket">Gün</div>
        </div>
        <div class="zaman-bolumu">
            <div class="sayi" id="saat">00</div>
            <div class="etiket">Saat</div>
        </div>
        <div class="zaman-bolumu">
            <div class="sayi" id="dakika">00</div>
            <div class="etiket">Dakika</div>
        </div>
        <div class="zaman-bolumu">
            <div class="sayi" id="saniye">00</div>
            <div class="etiket">Saniye</div>
        </div>
    </div>

<script>
    // Dinamik olarak bir sonraki yılın 1 Ocak tarihini hedef alalım
    const currentYear = new Date().getFullYear();
    const hedefTarih = new Date(`January 1, ${currentYear + 1} 00:00:00`).getTime();

    function geriSayimiGuncelle() {
        const simdi = new Date().getTime();
        const mesafe = hedefTarih - simdi;

        // Zaman dönüşüm hesaplamaları
        const gun = Math.floor(mesafe / (1000 * 60 * 60 * 24));
        const saat = Math.floor((mesafe % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const dakika = Math.floor((mesafe % (1000 * 60 * 60)) / (1000 * 60));
        const saniye = Math.floor((mesafe % (1000 * 60)) / 1000);

        // Ekran elementlerini güncelleme
        document.getElementById('gun').textContent = String(gun).padStart(2, '0');
        document.getElementById('saat').textContent = String(saat).padStart(2, '0');
        document.getElementById('dakika').textContent = String(dakika).padStart(2, '0');
        document.getElementById('saniye').textContent = String(saniye).padStart(2, '0');

        // Süre dolduğunda durdur
        if (mesafe < 0) {
            clearInterval(sayacInterval);
            document.querySelector('.sayac-kutusu').innerHTML = "<h2>Mutlu Yıllar! 🎉</h2>";
        }
    }

    // Her saniye çalıştır
    const sayacInterval = setInterval(geriSayimiGuncelle, 1000);
    geriSayimiGuncelle();
</script>
</body>
</html>

📌 Proje 14: Desen/Hafıza Eşleştirme Oyunu (Pattern Matching Game)

Bilgisayarın yaktığı renk dizisini hafızanızda tutup sırasıyla aynı kutulara tıklamanız gerekir. Her turda diziye yeni bir renk eklenerek oyun zorlaşır.
Dosya Adı: 14-hafiza-desen-eslestirme.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Desen Eşleştirme Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #222;
            color: white;
            text-align: center;
            margin: 0;
            padding-top: 30px;
        }
        .grid {
            display: grid;
            grid-template-columns: repeat(2, 130px);
            grid-template-rows: repeat(2, 130px);
            gap: 15px;
            justify-content: center;
            margin: 20px auto;
        }
        .box {
            border-radius: 10px;
            cursor: pointer;
            opacity: 0.6;
            transition: opacity 0.2s, transform 0.1s;
        }
        .box:active { transform: scale(0.95); }
        .red { background-color: #e74c3c; }
        .blue { background-color: #3498db; }
        .green { background-color: #2ecc71; }
        .yellow { background-color: #f1c40f; }
        .active { opacity: 1; box-shadow: 0 0 20px white; }
        button {
            padding: 10px 25px;
            font-size: 16px;
            background: white;
            color: #222;
            border: none;
            border-radius: 5px;
            font-weight: bold;
            cursor: pointer;
        }
    </style>
</head>
<body>

    <h1>Desen Hafıza Oyunu</h1>
    <p id="skorTablosu">Tur: 0</p>
    <button id="startBtn">Oyunu Başlat</button>

    <div class="grid">
        <div class="box red" id="0"></div>
        <div class="box blue" id="1"></div>
        <div class="box green" id="2"></div>
        <div class="box yellow" id="3"></div>
    </div>

<script>
    let bilgisayarSerisi = [];
    let oyuncuSerisi = [];
    let tur = 0;
    let oyunAktif = false;

    const boxes = document.querySelectorAll('.box');
    const startBtn = document.getElementById('startBtn');
    const skorTablosu = document.getElementById('skorTablosu');

    startBtn.addEventListener('click', () => {
        if (!oyunAktif) {
            oyunAktif = true;
            bilgisayarSerisi = [];
            tur = 0;
            startBtn.disabled = true;
            yeniTur();
        }
    });

    function yeniTur() {
        oyuncuSerisi = [];
        tur++;
        skorTablosu.textContent = `Tur: ${tur}`;
        bilgisayarSerisi.push(Math.floor(Math.random() * 4));
        seriyiOynat();
    }

    function seriyiOynat() {
        let i = 0;
        const interval = setInterval(() => {
            kutuyuYak(bilgisayarSerisi[i]);
            i++;
            if (i >= bilgisayarSerisi.length) {
                clearInterval(interval);
            }
        }, 600);
    }

    function kutuyuYak(id) {
        const box = document.getElementById(id);
        box.classList.add('active');
        setTimeout(() => box.classList.remove('active'), 300);
    }

    boxes.forEach(box => {
        box.addEventListener('click', (e) => {
            if (!oyunAktif || bilgisayarSerisi.length === 0) return;
            
            const secilenId = parseInt(e.target.id);
            kutuyuYak(secilenId);
            oyuncuSerisi.push(secilenId);

            // Tıklama kontrol doğrulaması
            const gecerliIndeks = oyuncuSerisi.length - 1;
            if (oyuncuSerisi[gecerliIndeks] !== bilgisayarSerisi[gecerliIndeks]) {
                alert(`❌ Yanlış Sıralama! Oyun bitti. Ulaştığınız Tur: ${tur}`);
                oyunAktif = false;
                startBtn.disabled = false;
                startBtn.textContent = "Tekrar Dene";
                return;
            }

            if (oyuncuSerisi.length === bilgisayarSerisi.length) {
                setTimeout(yeniTur, 1000);
            }
        });
    });
</script>
</body>
</html>

📌 Proje 15: Balon Patlatma Oyunu (Click Popper Game)

Ekranda rastgele konumlarda beliren kırmızı balonları süreniz bitmeden en hızlı şekilde tıklayarak patlatmaya çalıştığınız yüksek tempolu bir tık refleks oyunudur.
Dosya Adı: 15-balon-patlatma.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Balon Patlatma Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f5f6fa;
            margin: 0;
            overflow: hidden;
            text-align: center;
        }
        .header-panel {
            background: #2f3640;
            color: white;
            padding: 10px;
            display: flex;
            justify-content: space-around;
            font-size: 18px;
            font-weight: bold;
        }
        .oyun-alani {
            width: 100vw;
            height: calc(100vh - 50px);
            position: relative;
            background: #dcdde1;
            cursor: crosshair;
        }
        .balon {
            width: 50px;
            height: 65px;
            background: #e84118;
            border-radius: 50% 50% 50% 50% / 40% 40% 60% 60%;
            position: absolute;
            cursor: pointer;
            box-shadow: inset -5px -5px 10px rgba(0,0,0,0.2);
        }
        /* Balonun altındaki küçük düğüm çizgisi */
        .balon::after {
            content: "";
            position: absolute;
            bottom: -5px;
            left: 22px;
            width: 6px;
            height: 6px;
            background: #c23616;
            transform: rotate(45deg);
        }
        button {
            padding: 5px 15px;
            font-size: 16px;
            cursor: pointer;
        }
    </style>
</head>
<body>

    <div class="header-panel">
        <div>Skor: <span id="skorTxt">0</span></div>
        <div>Kalan Süre: <span id="sureTxt">20</span>s</div>
        <button id="gameActionBtn">Başlat</button>
    </div>
    
    <div class="oyun-alani" id="gameArea"></div>

<script>
    let skor = 0;
    let kalanSure = 20;
    let oyunInterval, balonInterval;
    let oyunDevamEdiyor = false;

    const gameArea = document.getElementById('gameArea');
    const skorTxt = document.getElementById('skorTxt');
    const sureTxt = document.getElementById('sureTxt');
    const gameActionBtn = document.getElementById('gameActionBtn');

    gameActionBtn.addEventListener('click', () => {
        if (!oyunDevamEdiyor) startGame();
    });

    function startGame() {
        skor = 0;
        kalanSure = 20;
        oyunDevamEdiyor = true;
        gameArea.innerHTML = '';
        skorTxt.textContent = skor;
        sureTxt.textContent = kalanSure;
        gameActionBtn.disabled = true;

        oyunInterval = setInterval(() => {
            kalanSure--;
            sureTxt.textContent = kalanSure;
            if (kalanSure <= 0) gameOver();
        }, 1000);

        balonInterval = setInterval(balonOlustur, 700);
    }

    function balonOlustur() {
        if (!oyunDevamEdiyor) return;

        const balon = document.createElement('div');
        balon.className = 'balon';

        // Oyun alanı boyut sınırlarına göre konum belirleme
        const maxX = gameArea.clientWidth - 60;
        const maxY = gameArea.clientHeight - 80;
        
        balon.style.left = Math.floor(Math.random() * maxX) + 'px';
        balon.style.top = Math.floor(Math.random() * maxY) + 'px';

        // Balona tıklandığında patlama aksiyonu
        balon.addEventListener('mousedown', () => {
            if (oyunDevamEdiyor) {
                skor++;
                skorTxt.textContent = skor;
                balon.remove();
            }
        });

        gameArea.appendChild(balon);

        // Balon tıklanmadıysa 2 saniye sonra ekrandan silinsin
        setTimeout(() => { if(balon.parentElement) balon.remove(); }, 2000);
    }

    function gameOver() {
        oyunDevamEdiyor = false;
        clearInterval(oyunInterval);
        clearInterval(balonInterval);
        gameActionBtn.disabled = false;
        gameActionBtn.textContent = "Tekrar Oyna";
        alert(`Oyun Bitti! Toplam Skorunuz: ${skor}`);
    }
</script>
</body>
</html>

Müfredat listemizdeki 16. ve 20. projeler arasındaki uygulamaların tüm HTML, CSS ve JavaScript kod bloklarını, tarayıcınızda doğrudan kopyalayıp çalıştırabileceğiniz şekilde tek bir dosya formatında hazırladım.


📌 Proje 16: Yüksek Alçak Kart Oyunu (Hi Lo Card Game)

Ekrandaki karttan bir sonraki çekilecek kartın değerinin daha mı yüksek daha mı düşük olacağını tahmin etmeye çalıştığınız bir deste oyunudur.
Dosya Adı: 16-yuksek-alcak-kart.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Yüksek Alçak Kart Oyunu</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: #1e3799;
            color: white;
            text-align: center;
            margin: 0;
            padding: 20px;
        }
        .game-container {
            background-color: #0a3d62;
            max-width: 400px;
            margin: 30px auto;
            padding: 30px;
            border-radius: 15px;
            box-shadow: 0 10px 25px rgba(0,0,0,0.4);
        }
        .card-display {
            width: 140px;
            height: 200px;
            background-color: white;
            color: black;
            border-radius: 10px;
            margin: 25px auto;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            font-size: 40px;
            font-weight: bold;
            box-shadow: 0 5px 15px rgba(0,0,0,0.3);
            position: relative;
        }
        .buttons {
            display: flex;
            justify-content: center;
            gap: 15px;
            margin-top: 20px;
        }
        button {
            padding: 12px 24px;
            font-size: 18px;
            font-weight: bold;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: 0.2s;
        }
        .high-btn { background-color: #2ecc71; color: white; }
        .low-btn { background-color: #e74c3c; color: white; }
        button:hover { opacity: 0.9; transform: translateY(-2px); }
        .info { margin-top: 20px; font-size: 18px; font-weight: bold; min-height: 25px; }
    </style>
</head>
<body>

<div class="game-container">
    <h2>🃏 Yüksek mi Alçak mı?</h2>
    <p>Bir sonraki kartın değeri mevcut karttan daha yüksek mi yoksa alçak mı olacak?</p>
    
    <div class="score">Skor: <span id="scoreTxt">0</span></div>
    
    <div class="card-display" id="cardDisplay">
        <div id="cardValue">7</div>
    </div>

    <div class="buttons">
        <button class="high-btn" id="highBtn">▲ YÜKSEK</button>
        <button class="low-btn" id="lowBtn">▼ ALÇAK</button>
    </div>

    <div class="info" id="infoTxt">Oyuna Başla!</div>
</div>

<script>
    let currentCardValue = 7;
    let score = 0;

    const cardValueDiv = document.getElementById('cardValue');
    const scoreTxt = document.getElementById('scoreTxt');
    const infoTxt = document.getElementById('infoTxt');

    function playRound(guess) {
        // 1 ile 13 arası rastgele yeni bir kart değeri seçiyoruz (As=1, Vale=11, Kız=12, Papaz=13)
        let nextCardValue = Math.floor(Math.random() * 13) + 1;
        
        // Eşitlik durumunda tekrar zar atalım ki oyun kilitlenmesin
        while (nextCardValue === currentCardValue) {
            nextCardValue = Math.floor(Math.random() * 13) + 1;
        }

        cardValueDiv.textContent = nextCardValue;

        let isHigher = nextCardValue > currentCardValue;
        let won = (guess === 'high' && isHigher) || (guess === 'low' && !isHigher);

        if (won) {
            score++;
            scoreTxt.textContent = score;
            infoTxt.textContent = `🎉 Doğru! Yeni kart: ${nextCardValue}.`;
            infoTxt.style.color = '#2ecc71';
        } else {
            score = 0;
            scoreTxt.textContent = score;
            infoTxt.textContent = `❌ Yanlış! Yeni kart: ${nextCardValue}. Skor Sıfırlandı!`;
            infoTxt.style.color = '#e74c3c';
        }

        currentCardValue = nextCardValue;
    }

    document.getElementById('highBtn').addEventListener('click', () => playRound('high'));
    document.getElementById('lowBtn').addEventListener('click', () => playRound('low'));
</script>
</body>
</html>

📌 Proje 17: JavaScript Araba Yarışı Oyunu (Car Driving Game)

Klavyedeki sağ ve sol yön tuşlarıyla kendi arabanızı kontrol edip, yukarıdan gelen engellerden kaçarak skor topladığınız dikey bir refleks oyunudur.
Dosya Adı: 17-araba-yarisi.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Araba Driving Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background: #2c3e50;
            margin: 0;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }
        .score { color: white; font-size: 24px; font-weight: bold; margin-bottom: 10px; }
        .game-area {
            width: 300px;
            height: 500px;
            background: #34495e;
            position: relative;
            overflow: hidden;
            border: 4px solid white;
            border-radius: 8px;
        }
        .my-car {
            width: 40px;
            height: 70px;
            background: #e74c3c;
            border-radius: 5px;
            position: absolute;
            bottom: 20px;
            left: 130px;
        }
        .enemy {
            width: 40px;
            height: 70px;
            background: #f1c40f;
            border-radius: 5px;
            position: absolute;
            top: -70px;
        }
        .start-screen {
            position: absolute;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.8);
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 20px;
            font-weight: bold;
            cursor: pointer;
            z-index: 2;
            text-align: center;
        }
    </style>
</head>
<body>

    <div class="score">Skor: <span id="scoreBox">0</span></div>
    <div class="game-area">
        <div class="start-screen" id="startScreen">Oyunu Başlatmak İçin Tıklayın<br><br>Yön Tuşları: A / D veya Sol / Sağ</div>
        <div class="my-car" id="myCar"></div>
        <div class="enemy" id="enemyCard"></div>
    </div>

<script>
    const gameArea = document.querySelector('.game-area');
    const myCar = document.getElementById('myCar');
    const enemy = document.getElementById('enemyCard');
    const startScreen = document.getElementById('startScreen');
    const scoreBox = document.getElementById('scoreBox');

    let score = 0;
    let carX = 130;
    let enemyY = -70;
    let enemyX = 130;
    let gameActive = false;
    let keys = {};

    startScreen.addEventListener('click', startGame);
    window.addEventListener('keydown', (e) => keys[e.key] = true);
    window.addEventListener('keyup', (e) => keys[e.key] = false);

    function startGame() {
        startScreen.style.display = 'none';
        gameActive = true;
        score = 0;
        carX = 130;
        enemyY = -70;
        enemyX = Math.floor(Math.random() * 260);
        enemy.style.left = enemyX + 'px';
        scoreBox.textContent = score;
        requestAnimationFrame(updateGame);
    }

    function updateGame() {
        if (!gameActive) return;

        // Oyuncu araba hareketi
        if ((keys['ArrowLeft'] || keys['a']) && carX > 0) carX -= 5;
        if ((keys['ArrowRight'] || keys['d']) && carX < 260) carX += 5;
        myCar.style.left = carX + 'px';

        // Engel (Düşman araba) hareketi
        enemyY += 6;
        if (enemyY > 500) {
            enemyY = -70;
            enemyX = Math.floor(Math.random() * 260);
            enemy.style.left = enemyX + 'px';
            score++;
            scoreBox.textContent = score;
        }
        enemy.style.top = enemyY + 'px';

        // Çarpışma Testi Algoritması (Collision Detection)
        if (enemyY + 70 > 410 && enemyY < 480) { // Y ekseni çakışması
            if (enemyX + 40 > carX && enemyX < carX + 40) { // X ekseni çakışması
                gameActive = false;
                startScreen.style.display = 'flex';
                startScreen.innerHTML = `Oyun Bitti!<br>Skorunuz: ${score}<br>Tekrar oynamak için tıklayın`;
            }
        }

        if (gameActive) requestAnimationFrame(updateGame);
    }
</script>
</body>
</html>

📌 Proje 18: Giriş Alanı Karakter Sayacı (Character Counter)

Kullanıcı metin alanına yazı girdikçe kaç karakter yazdığını gösterir ve önceden belirlenen sınıra ulaşıldığında uyarı vererek daha fazla giriş yapılmasını engeller.
Dosya Adı: 18-karakter-sayaci.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Karakter Sayacı</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #f8f9fa;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .counter-card {
            background: white;
            padding: 25px;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.05);
            width: 450px;
        }
        h3 { margin-top: 0; color: #333; }
        textarea {
            width: 100%;
            height: 120px;
            padding: 10px;
            font-size: 16px;
            border: 2px solid #ccc;
            border-radius: 6px;
            box-sizing: border-box;
            resize: none;
            transition: 0.2s;
        }
        textarea:focus { border-color: #3498db; outline: none; }
        .status-bar {
            display: flex;
            justify-content: space-between;
            margin-top: 10px;
            font-size: 15px;
            font-weight: bold;
            color: #555;
        }
        .warning { color: #e74c3c !important; }
    </style>
</head>
<body>

<div class="counter-card">
    <h3>📝 Karakter Sayacı (Tweet Sınırı)</h3>
    <textarea id="txtArea" placeholder="Mesajınızı buraya yazın..." maxlength="140"></textarea>
    <div class="status-bar">
        <div id="infoMsg">Kalan Karakter: 140</div>
        <div><span id="charCount">0</span> / 140</div>
    </div>
</div>

<script>
    const txtArea = document.getElementById('txtArea');
    const charCount = document.getElementById('charCount');
    const infoMsg = document.getElementById('infoMsg');
    const maxChars = 140;

    txtArea.addEventListener('input', () => {
        let currentLength = txtArea.value.length;
        let remaining = maxChars - currentLength;

        charCount.textContent = currentLength;
        infoMsg.textContent = `Kalan Karakter: ${remaining}`;

        // Sınır durum kontrolüne göre arayüz renklendirmesi
        if (remaining <= 20) {
            charCount.classList.add('warning');
            infoMsg.classList.add('warning');
        } else {
            charCount.classList.remove('warning');
            infoMsg.classList.remove('warning');
        }
    });
</script>
</body>
</html>

📌 Proje 19: Otomatik Kayan İçerik Kutusu (Element Scroller)

Haber sitelerinde yer alan kayan yazılar gibidir. Liste elemanları sürekli yukarı kayar, kullanıcı fareyi üzerine getirdiğinde durur, çektiğinde kaldığı yerden akmaya devam eder.
Dosya Adı: 19-otomatik-kayan-icerik.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Otomatik Kayan İçerik</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f1f2f6;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .scroller-box {
            width: 350px;
            height: 150px;
            background: white;
            border: 2px solid #dfe4ea;
            border-radius: 8px;
            overflow: hidden;
            position: relative;
            box-shadow: 0 4px 10px rgba(0,0,0,0.05);
        }
        .scroller-content {
            position: absolute;
            width: 100%;
            margin: 0;
            padding: 0;
            list-style: none;
            top: 0;
        }
        .scroller-content li {
            padding: 15px;
            border-bottom: 1px solid #f1f2f6;
            color: #2f3542;
            font-weight: bold;
        }
    </style>
</head>
<body>

<div class="scroller-box" id="scrollerBox">
    <ul class="scroller-content" id="scrollerContent">
        <li>🔥 JavaScript ES6+ Özellikleri Yayında!</li>
        <li>⚡ Asenkron Programlama Teknikleri Öğrenin.</li>
        <li>🚀 45 Proje ile Web Geliştirme Eğitimi.</li>
        <li>🤖 Yapay Zeka Entegrasyonları Başladı.</li>
    </ul>
</div>

<script>
    const box = document.getElementById('scrollerBox');
    const content = document.getElementById('scrollerContent');
    
    let scrollSpeed = 1; 
    let scrollInterval;
    let currentTop = 0;

    // Sonsuz döngü hissi yaratmak için ilk listenin kopyasını sonuna ekliyoruz
    content.innerHTML += content.innerHTML;

    function startScrolling() {
        scrollInterval = setInterval(() => {
            currentTop -= scrollSpeed;
            
            // Listenin tam yarısına gelindiğinde (orijinal içerik bittiğinde) başa sarıyoruz
            if (Math.abs(currentTop) >= content.scrollHeight / 2) {
                currentTop = 0;
            }
            content.style.top = currentTop + 'px';
        }, 30);
    }

    // Fare olaylarını pusuya yatıp dinleme (Hover kontrolü)
    box.addEventListener('mouseover', () => clearInterval(scrollInterval));
    box.addEventListener('mouseout', startScrolling);

    // Akışı başlat
    startScrolling();
</script>
</body>
</html>

📌 Proje 20: Sürükle Bırak Çöp Kutusu (DOM Trash Bin)

Web arayüzündeki öğeleri farenizle tutup çöp kutusu görselinin üzerine sürükleyerek sayfadan dinamik olarak yok etmenizi (Element.remove()) sağlayan yerleşik HTML5 Drag & Drop projesidir.
Dosya Adı: 20-surukle-birak-cop-kutusu.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sürükle Bırak Çöp Kutusu</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f7f9fa;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            gap: 50px;
        }
        .items-panel {
            width: 250px;
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        .drag-item {
            background: #3498db;
            color: white;
            padding: 15px;
            border-radius: 6px;
            cursor: grab;
            font-weight: bold;
            text-align: center;
            box-shadow: 0 3px 6px rgba(0,0,0,0.1);
        }
        .drag-item:active { cursor: grabbing; }
        .trash-bin {
            width: 180px;
            height: 180px;
            border: 4px dashed #95a5a6;
            border-radius: 12px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            color: #7f8c8d;
            font-weight: bold;
            font-size: 18px;
            transition: 0.2s;
            background: white;
        }
        .drag-over {
            border-color: #e74c3c;
            background-color: #fadbd8;
            color: #c0392b;
            transform: scale(1.05);
        }
    </style>
</head>
<body>

    <div class="items-panel">
        <div class="drag-item" draggable="true" id="item1">🗒️ Eski Notlar</div>
        <div class="drag-item" draggable="true" id="item2">📂 Geçici Dosya</div>
        <div class="drag-item" draggable="true" id="item3">🗑️ Çöp Metin Bloğu</div>
    </div>

    <div class="trash-bin" id="trashBin">
        <span>🗑️</span>
        <span style="margin-top: 10px;">Çöp Kutusu</span>
    </div>

<script>
    const items = document.querySelectorAll('.drag-item');
    const trashBin = document.getElementById('trashBin');

    items.forEach(item => {
        item.addEventListener('dragstart', (e) => {
            // Sürüklenen elemanın ID'sini hafızaya alıyoruz
            e.dataTransfer.setData('text/plain', e.target.id);
        });
    });

    // Çöp kutusu üzerindeki olayların yönetilmesi
    trashBin.addEventListener('dragover', (e) => {
        e.preventDefault(); // Varsayılan engellemeyi kaldırarak bırakmaya izin veriyoruz
        trashBin.classList.add('drag-over');
    });

    trashBin.addEventListener('dragleave', () => {
        trashBin.classList.remove('drag-over');
    });

    trashBin.addEventListener('drop', (e) => {
        e.preventDefault();
        trashBin.classList.remove('drag-over');
        
        // Taşınan elemanın ID'sini çekip DOM'dan yok ediyoruz
        const droppedItemId = e.dataTransfer.getData('text/plain');
        const droppedElement = document.getElementById(droppedItemId);
        
        if (droppedElement) {
            droppedElement.remove();
            alert("Öğe başarıyla imha edildi!");
        }
    });
</script>
</body>
</html>

Müfredat listemizdeki 21. ve 25. projeler arasındaki uygulamaların tüm HTML, CSS ve JavaScript kod bloklarını, tarayıcınızda doğrudan kopyalayıp çalıştırabileceğiniz şekilde tek bir dosya formatında hazırladım.


📌 Proje 21: Yalnızca JavaScript Tık Sayacı (Just JavaScript Click Counter)

HTML dosyasında hiçbir buton veya yazı yokken, tüm elementlerin ve sayaç mekanizmasının tamamen JavaScript koduyla havadan var edildiği (document.createElement) bir projedir.
Dosya Adı: 21-sadece-js-tik-sayaci.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Yalnızca JavaScript Tık Sayacı</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #f1f2f6;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        /* Dinamik oluşturulacak kartın stilleri */
        .js-card {
            background: white;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
            text-align: center;
            width: 300px;
        }
        .js-counter {
            font-size: 48px;
            font-weight: bold;
            color: #2f3542;
            margin: 20px 0;
        }
        .js-btn {
            padding: 10px 20px;
            font-size: 16px;
            font-weight: bold;
            color: white;
            background-color: #ff4757;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: background 0.2s;
        }
        .js-btn:hover { background-color: #ff6b81; }
    </style>
</head>
<body>

<!-- HTML İÇERİĞİ TAMAMEN BOŞTUR. HER ŞEYİ JAVASCRIPT OLUŞTURACAK -->

<script>
    // 1. Ana kart kutusunu oluşturuyoruz
    const card = document.createElement('div');
    card.className = 'js-card';

    // 2. Başlığı oluşturuyoruz
    const title = document.createElement('h2');
    title.textContent = 'Saf JS Sayaç';
    title.style.margin = '0';
    title.style.color = '#747d8c';

    // 3. Sayacı tutacak alanı oluşturuyoruz
    let count = 0;
    const counterDisplay = document.createElement('div');
    counterDisplay.className = 'js-counter';
    counterDisplay.textContent = count;

    // 4. Butonu oluşturuyoruz
    const button = document.createElement('button');
    button.className = 'js-btn';
    button.textContent = 'Tıkla ve Arttır 👆';

    // Butona tıklama olayı (Event) ekliyoruz
    button.addEventListener('click', () => {
        count++;
        counterDisplay.textContent = count;
    });

    // 5. Oluşturulan tüm elementleri iç içe ekleyip sayfaya bağlıyoruz
    card.appendChild(title);
    card.appendChild(counterDisplay);
    card.appendChild(button);
    document.body.appendChild(card);
</script>
</body>
</html>

📌 Proje 22: Görsel Pop-up Penceresi (Image Popup Window)

Sayfadaki herhangi bir küçük resme tıklandığında, resmin büyük halini ekranı kaplayan siyah şeffaf bir katman üzerinde açan galeri odaklı bir projedir.
Dosya Adı: 22-gorsel-popup.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Görsel Pop-up Penceresi</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f5f6fa;
            margin: 0;
            padding: 40px;
            text-align: center;
        }
        .gallery {
            display: flex;
            justify-content: center;
            gap: 20px;
            margin-top: 30px;
        }
        .gallery img {
            width: 200px;
            height: 150px;
            object-fit: cover;
            border-radius: 8px;
            cursor: pointer;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
            transition: transform 0.2s;
        }
        .gallery img:hover { transform: scale(1.05); }
        
        /* Arka Plan Pop-up Kapsayıcısı */
        .popup-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            background-color: rgba(0, 0, 0, 0.85);
            display: flex;
            justify-content: center;
            align-items: center;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.3s ease;
            z-index: 10;
        }
        .popup-overlay.open {
            opacity: 1;
            pointer-events: auto;
        }
        .popup-img {
            max-width: 80%;
            max-height: 80%;
            border-radius: 8px;
            box-shadow: 0 5px 25px rgba(0,0,0,0.5);
            transform: scale(0.8);
            transition: transform 0.3s ease;
        }
        .popup-overlay.open .popup-img { transform: scale(1); }
        .close-btn {
            position: absolute;
            top: 20px;
            right: 30px;
            color: white;
            font-size: 40px;
            cursor: pointer;
            user-select: none;
        }
    </style>
</head>
<body>

    <h1>📷 Resim Galerisi Pop-up Sistemi</h1>
    <p>Büyük halini görmek istediğiniz resmin üzerine tıklayın.</p>

    <div class="gallery">
        <!-- Test amaçlı farklı renklere sahip hazır çizim görselleri kullanılmıştır -->
        <img src="https://picsum.photos" alt="Doğa">
        <img src="https://picsum.photos" alt="Yazı Masası">
        <img src="https://picsum.photos" alt="Anahtarlık">
    </div>

    <!-- Gizli Pop-up Alanı -->
    <div class="popup-overlay" id="popupOverlay">
        <span class="close-btn" id="closeBtn">&times;</span>
        <img src="" class="popup-img" id="popupImg" alt="Büyük Görsel">
    </div>

<script>
    const galleryImages = document.querySelectorAll('.gallery img');
    const popupOverlay = document.getElementById('popupOverlay');
    const popupImg = document.getElementById('popupImg');
    const closeBtn = document.getElementById('closeBtn');

    galleryImages.forEach(img => {
        img.addEventListener('click', () => {
            popupImg.src = img.src; // Tıklanan resmin kaynağını pop-up alanına aktarıyoruz
            popupOverlay.classList.add('open');
        });
    });

    // Kapatma butonuna veya siyah arka plana tıklandığında pop-up penceresini gizle
    closeBtn.addEventListener('click', () => popupOverlay.classList.remove('open'));
    popupOverlay.addEventListener('click', (e) => {
        if (e.target === popupOverlay) {
            popupOverlay.classList.remove('open');
        }
    });
</script>
</body>
</html>

📌 Proje 23: Modal Pencere Görünümü (JavaScript Modal Popup Example)

Kullanıcı bir işleme tıkladığında sayfa akışını keserek ekrana gelen, CSS sınıfları (classList.add/remove) aracılığıyla açılıp kapanan uyarı ve bildirim penceresidir.
Dosya Adı: 23-modal-pencere.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Modal Pencere Görünümü</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #f0f2f5;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .trigger-btn {
            background-color: #1e90ff;
            color: white;
            border: none;
            padding: 14px 28px;
            font-size: 16px;
            font-weight: bold;
            border-radius: 6px;
            cursor: pointer;
            box-shadow: 0 4px 10px rgba(30,144,255,0.3);
        }
        .trigger-btn:hover { background-color: #117ee6; }
        
        /* Modal Arka Katman */
        .modal-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            background-color: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.25s ease;
        }
        .modal-container.show {
            opacity: 1;
            pointer-events: auto;
        }
        
        /* Modal Gövdesi */
        .modal-box {
            background: white;
            padding: 25px;
            border-radius: 10px;
            width: 400px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            text-align: left;
            transform: translateY(-30px);
            transition: transform 0.25s ease;
        }
        .modal-container.show .modal-box { transform: translateY(0); }
        .modal-box h3 { margin-top: 0; color: #2f3542; }
        .modal-box p { color: #57606f; line-height: 1.5; }
        .action-area {
            display: flex;
            justify-content: flex-end;
            gap: 10px;
            margin-top: 20px;
        }
        .close-modal-btn {
            background-color: #ced6e0;
            color: #2f3542;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
        }
        .close-modal-btn:hover { background-color: #a4b0be; }
    </style>
</head>
<body>

    <button class="trigger-btn" id="openModalBtn">⚙️ Ayarları Düzenle</button>

    <!-- Kapsayıcı Modal -->
    <div class="modal-container" id="modalContainer">
        <div class="modal-box">
            <h3>Sistem Bildirimi</h3>
            <p>Değişiklikleri kaydetmek istediğinizden emin misiniz? Bu işlem geri alınamaz ve tüm aktif oturumlar güncellenir.</p>
            <div class="action-area">
                <button class="close-modal-btn" id="cancelBtn">İptal</button>
                <button class="close-modal-btn" style="background-color: #2ed573; color: white;" id="confirmBtn">Onayla</button>
            </div>
        </div>
    </div>

<script>
    const openModalBtn = document.getElementById('openModalBtn');
    const modalContainer = document.getElementById('modalContainer');
    const cancelBtn = document.getElementById('cancelBtn');
    const confirmBtn = document.getElementById('confirmBtn');

    openModalBtn.addEventListener('click', () => {
        modalContainer.classList.add('show');
    });

    function closeModal() {
        modalContainer.classList.remove('show');
    }

    cancelBtn.addEventListener('click', closeModal);
    confirmBtn.addEventListener('click', () => {
        alert("İşlem onaylandı!");
        closeModal();
    });

    // Boş arka plana tıklandığında kapatma kontrolü
    modalContainer.addEventListener('click', (e) => {
        if (e.target === modalContainer) closeModal();
    });
</script>
</body>
</html>

📌 Proje 24: Resim Galerisi Slayt Gösterisi (Image Carousel JavaScript)

Önceki ve sonraki butonlarına basıldığında listelenen görselleri sırasıyla değiştiren ve alt kısımdaki yuvarlak düğmelerle (indikatörler) senkronize çalışan bir slider projesidir.
Dosya Adı: 24-resim-galerisi-slider.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Resim Slayt Gösterisi</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f7f9fa;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding-top: 50px;
            margin: 0;
        }
        .carousel-wrapper {
            position: relative;
            width: 500px;
            height: 330px;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 8px 20px rgba(0,0,0,0.15);
        }
        .slide {
            width: 100%;
            height: 100%;
            object-fit: cover;
            display: none;
        }
        .slide.active { display: block; }
        
        /* Kontrol Okları */
        .arrow-btn {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            background: rgba(0,0,0,0.5);
            color: white;
            border: none;
            width: 40px;
            height: 40px;
            font-size: 20px;
            font-weight: bold;
            cursor: pointer;
            border-radius: 50%;
            user-select: none;
        }
        .arrow-btn:hover { background: rgba(0,0,0,0.8); }
        .prev { left: 15px; }
        .next { right: 15px; }
        
        /* Noktalı İndikatör Alanı */
        .dots-panel {
            display: flex;
            gap: 8px;
            margin-top: 15px;
        }
        .dot {
            width: 12px;
            height: 12px;
            background-color: #ccc;
            border-radius: 50%;
            cursor: pointer;
            transition: background 0.2s;
        }
        .dot.active { background-color: #2f3542; }
    </style>
</head>
<body>

    <h2>🎬 JavaScript Akıllı Slider</h2>
    
    <div class="carousel-wrapper">
        <img src="https://picsum.photos" class="slide active" alt="Görsel 1">
        <img src="https://picsum.photos" class="slide" alt="Görsel 2">
        <img src="https://picsum.photos" class="slide" alt="Görsel 3">
        
        <button class="arrow-btn prev" id="prevBtn">&#10094;</button>
        <button class="arrow-btn next" id="nextBtn">&#10095;</button>
    </div>

    <div class="dots-panel" id="dotsPanel"></div>

<script>
    const slides = document.querySelectorAll('.slide');
    const prevBtn = document.getElementById('prevBtn');
    const nextBtn = document.getElementById('nextBtn');
    const dotsPanel = document.getElementById('dotsPanel');
    
    let currentIndex = 0;

    // Alt kısımdaki noktaları dinamik olarak resim sayısı kadar oluşturuyoruz
    slides.forEach((_, index) => {
        const dot = document.createElement('div');
        dot.className = 'dot' + (index === 0 ? ' active' : '');
        dot.addEventListener('click', () => changeSlide(index));
        dotsPanel.appendChild(dot);
    });

    const dots = document.querySelectorAll('.dot');

    function changeSlide(newIndex) {
        // Sınır kontrolü (Başa sarma veya sona sarma)
        if (newIndex >= slides.length) newIndex = 0;
        if (newIndex < 0) newIndex = slides.length - 1;

        // Mevcut aktifleri kaldır
        slides[currentIndex].classList.remove('active');
        dots[currentIndex].classList.remove('active');

        // Yeni aktifi ata
        currentIndex = newIndex;
        slides[currentIndex].classList.add('active');
        dots[currentIndex].classList.add('active');
    }

    nextBtn.addEventListener('click', () => changeSlide(currentIndex + 1));
    prevBtn.addEventListener('click', () => changeSlide(currentIndex - 1));
</script>
</body>
</html>

📌 Proje 25: Yıldız Değerlendirme Sistemi (Star Rating Project)

Kullanıcı fareyi yıldızların üzerine getirdiğinde geçici puanı gösteren (mouseover), tıkladığında ise puanı kilitleyip sunucuya göndermeye hazır hale getiren interaktif puanlama arayüzüdür.
Dosya Adı: 25-yildiz-derecelendirme.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Yıldız Değerlendirme Sistemi</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f1f2f6;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }
        .rating-box {
            background: white;
            padding: 25px;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.05);
            text-align: center;
        }
        .stars {
            display: flex;
            gap: 10px;
            justify-content: center;
            margin: 15px 0;
            font-size: 35px;
            color: #ccc;
            cursor: pointer;
        }
        /* Seçili veya üzerine gelinen yıldız rengi */
        .star.hovered, .star.selected {
            color: #f1c40f;
        }
        .status-txt {
            font-size: 16px;
            font-weight: bold;
            color: #57606f;
            min-height: 22px;
        }
    </style>
</head>
<body>

<div class="rating-box">
    <h3>Uygulamayı Değerlendirin</h3>
    <div class="stars" id="starContainer">
        <span class="star" data-value="1">&#9733;</span>
        <span class="star" data-value="2">&#9733;</span>
        <span class="star" data-value="3">&#9733;</span>
        <span class="star" data-value="4">&#9733;</span>
        <span class="star" data-value="5">&#9733;</span>
    </div>
    <div class="status-txt" id="statusTxt">Puan vermek için yıldız seçin.</div>
</div>

<script>
    const stars = document.querySelectorAll('.star');
    const statusTxt = document.getElementById('statusTxt');
    let lockedRating = 0; // Tıklanıp sabitlenen puan

    stars.forEach(star => {
        // 1. Fare üzerine geldiğinde geçici renklendirme
        star.addEventListener('mouseover', function() {
            const currentHoverValue = parseInt(this.getAttribute('data-value'));
            highlightStars(currentHoverValue, 'hovered');
        });

        // 2. Fare çekildiğinde geçici rengi sil
        star.addEventListener('mouseout', function() {
            clearHighlights('hovered');
        });

        // 3. Tıklandığında puanı kilitle
        star.addEventListener('click', function() {
            lockedRating = parseInt(this.getAttribute('data-value'));
            clearHighlights('selected');
            highlightStars(lockedRating, 'selected');
            statusTxt.textContent = `Teşekkürler! Puanınız: ${lockedRating} / 5`;
            statusTxt.style.color = '#2ed573';
        });
    });

    function highlightStars(value, className) {
        stars.forEach(star => {
            if (parseInt(star.getAttribute('data-value')) <= value) {
                star.classList.add(className);
            }
        });
    }

    function clearHighlights(className) {
        stars.forEach(star => star.classList.remove(className));
    }
</script>
</body>
</html>

Müfredat listemizdeki 26. ve 30. projeler arasındaki uygulamaların tüm HTML, CSS ve JavaScript kod bloklarını, tarayıcınızda doğrudan kopyalayıp çalıştırabileceğiniz şekilde tek bir dosya formatında hazırladım.


📌 Proje 26: Uçak Bombardıman Oyunu (Plane Bomber)

Klavyedeki sağ ve sol yön tuşlarıyla üstteki uçağı hareket ettirip, boşluk (Space) tuşuna basarak bombalar bıraktığınız ve aşağıdaki hareketli hedefi vurmaya çalıştığınız bir DOM oyunudur.
Dosya Adı: 26-ucak-bombardiman.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Uçak Bombardıman Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background: #2c3e50;
            color: white;
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            overflow: hidden;
        }
        .info-panel { font-size: 20px; font-weight: bold; margin-bottom: 10px; }
        .game-screen {
            width: 500px;
            height: 400px;
            background: #34495e;
            border: 4px solid #ecf0f1;
            position: relative;
            border-radius: 8px;
        }
        .plane {
            width: 50px;
            height: 25px;
            background: #e74c3c;
            position: absolute;
            top: 20px;
            left: 225px;
            border-radius: 4px;
        }
        .target {
            width: 60px;
            height: 20px;
            background: #2ecc71;
            position: absolute;
            bottom: 10px;
            left: 0;
            border-radius: 3px;
        }
        .bomb {
            width: 10px;
            height: 20px;
            background: #f1c40f;
            position: absolute;
            border-radius: 50% 50% 20% 20%;
        }
    </style>
</head>
<body>

    <div class="info-panel">Skor: <span id="scoreTxt">0</span> | Sağ/Sol Ok Tuşları: Hareket | Boşluk: Bomba</div>
    <div class="game-screen" id="gameScreen">
        <div class="plane" id="plane"></div>
        <div class="target" id="target"></div>
    </div>

<script>
    const screen = document.getElementById('gameScreen');
    const plane = document.getElementById('plane');
    const target = document.getElementById('target');
    const scoreTxt = document.getElementById('scoreTxt');

    let planeX = 225;
    let targetX = 0;
    let targetSpeed = 3;
    let score = 0;
    let keys = {};
    let isBombActive = false;

    window.addEventListener('keydown', (e) => keys[e.key] = true);
    window.addEventListener('keyup', (e) => keys[e.key] = false);

    function gameLoop() {
        // Uçak Hareketi Kontrolü
        if (keys['ArrowLeft'] && planeX > 0) planeX -= 5;
        if (keys['ArrowRight'] && planeX < 450) planeX += 5;
        plane.style.left = planeX + 'px';

        // Hedef Hareketi
        targetX += targetSpeed;
        if (targetX > 440 || targetX < 0) {
            targetSpeed = -targetSpeed; // Duvara çarpınca geri dönsün
        }
        target.style.left = targetX + 'px';

        // Bomba Atma Kontrolü
        if (keys[' '] && !isBombActive) {
            dropBomb();
        }

        requestAnimationFrame(gameLoop);
    }

    function dropBomb() {
        isBombActive = true;
        const bomb = document.createElement('div');
        bomb.className = 'bomb';
        bomb.style.left = (planeX + 20) + 'px';
        bomb.style.top = '45px';
        screen.appendChild(bomb);

        let bombY = 45;
        const bombInterval = setInterval(() => {
            bombY += 5;
            bomb.style.top = bombY + 'px';

            // Zemin Kontrolü
            if (bombY > 370) {
                // Çarpışma Testi (Bomba hedefi vurdu mu?)
                let bombX = parseInt(bomb.style.left);
                if (bombX >= targetX && bombX <= targetX + 60) {
                    score++;
                    scoreTxt.textContent = score;
                    targetX = 0; // Hedefi sıfırla (vurulma etkisi)
                }
                clearInterval(bombInterval);
                bomb.remove();
                isBombActive = false;
            }
        }, 20);
    }

    requestAnimationFrame(gameLoop);
</script>
</body>
</html>

📌 Proje 27: Kutu Kovalama Oyunu (Chaser Box Game)

Siz yön tuşlarıyla mavi kutuyu hareket ettirirken, harita üzerinde rastgele sürekli yön değiştiren kırmızı kutuyu yakalamaya çalıştığınız eğlenceli bir yakalama oyunudur.
Dosya Adı: 27-kutu-kovalama.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kutu Kovalama Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f1f2f6;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }
        .hud { font-size: 22px; font-weight: bold; margin-bottom: 10px; color: #2f3542; }
        .arena {
            width: 400px;
            height: 400px;
            background-color: white;
            border: 4px solid #2f3542;
            position: relative;
            border-radius: 8px;
            box-shadow: 0 8px 16px rgba(0,0,0,0.1);
        }
        .player-box {
            width: 30px;
            height: 30px;
            background-color: #3498db;
            position: absolute;
            border-radius: 4px;
        }
        .enemy-box {
            width: 30px;
            height: 30px;
            background-color: #e74c3c;
            position: absolute;
            border-radius: 4px;
            transition: all 0.3s linear;
        }
    </style>
</head>
<body>

    <div class="hud">Skorunuz: <span id="scoreTxt">0</span></div>
    <div class="arena" id="arena">
        <div class="player-box" id="player" style="top: 0px; left: 0px;"></div>
        <div class="enemy-box" id="enemy" style="top: 200px; left: 200px;"></div>
    </div>

<script>
    const player = document.getElementById('player');
    const enemy = document.getElementById('enemy');
    const scoreTxt = document.getElementById('scoreTxt');

    let pX = 0, pY = 0;
    let eX = 200, eY = 200;
    let score = 0;
    const size = 30;
    const maxBound = 370;

    // Oyuncu Hareketi (Yön tuşları)
    window.addEventListener('keydown', (e) => {
        switch (e.key) {
            case 'ArrowUp': if (pY > 0) pY -= 15; break;
            case 'ArrowDown': if (pY < maxBound) pY += 15; break;
            case 'ArrowLeft': if (pX > 0) pX -= 15; break;
            case 'ArrowRight': if (pX < maxBound) pX += 15; break;
        }
        player.style.left = pX + 'px';
        player.style.top = pY + 'px';
        checkCollision();
    });

    // Kırmızı kutunun yapay zeka hareketi (Her 400ms'de rastgele kaçar)
    setInterval(() => {
        const directions = [-30, 0, 30];
        eX += directions[Math.floor(Math.random() * 3)];
        eY += directions[Math.floor(Math.random() * 3)];

        // Arena dışına taşma kontrolü
        if (eX < 0) eX = 0; if (eX > maxBound) eX = maxBound;
        if (eY < 0) eY = 0; if (eY > maxBound) eY = maxBound;

        enemy.style.left = eX + 'px';
        enemy.style.top = eY + 'px';
        checkCollision();
    }, 400);

    // Çarpışma / Yakalama Algılama
    function checkCollision() {
        const xDist = Math.abs(pX - eX);
        const yDist = Math.abs(pY - eY);

        if (xDist < size && yDist < size) {
            score++;
            scoreTxt.textContent = score;
            // Yakalanınca kırmızı kutuyu rastgele bir yere ışınla
            eX = Math.floor(Math.random() * (maxBound / 30)) * 30;
            eY = Math.floor(Math.random() * (maxBound / 30)) * 30;
            enemy.style.left = eX + 'px';
            enemy.style.top = eY + 'px';
        }
    }
</script>
</body>
</html>

📌 Proje 28: Bilgi İpucu Kutusu (Tooltip Popup)

Sayfadaki nesnelerin üzerine gelindiğinde, farenin tam konumunu (clientX/clientY) izleyerek dinamik bir açıklama balonu açan kullanıcı deneyimi aracıdır.
Dosya Adı: 28-tooltip-popup.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bilgi İpucu Kutusu (Tooltip)</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #f5f6fa;
            padding: 50px;
            text-align: center;
        }
        .container {
            margin-top: 100px;
            display: flex;
            justify-content: center;
            gap: 40px;
        }
        .tooltip-target {
            padding: 15px 30px;
            background-color: #9b59b6;
            color: white;
            font-weight: bold;
            border-radius: 6px;
            cursor: help;
            user-select: none;
        }
        /* Gizli Dinamik Tooltip Gövdesi */
        .custom-tooltip {
            position: fixed;
            background-color: #2c3e50;
            color: #ecf0f1;
            padding: 8px 12px;
            font-size: 13px;
            border-radius: 4px;
            pointer-events: none;
            display: none;
            z-index: 1000;
            box-shadow: 0 4px 8px rgba(0,0,0,0.15);
        }
    </style>
</head>
<body>

    <h2>📌 Akıllı Fare Takipçi Tooltip</h2>
    <p>Butonların üzerine farenizle gelin ve hareketi izleyin.</p>

    <div class="container">
        <div class="tooltip-target" data-tip="Bu buton JavaScript kullanarak hesaplama yapar.">Hesaplama</div>
        <div class="tooltip-target" data-tip="Verileriniz yerel hafızada şifreli saklanır.">Güvenlik Bilgisi</div>
    </div>

    <div class="custom-tooltip" id="tooltipBox">İpucu Metni</div>

<script>
    const targets = document.querySelectorAll('.tooltip-target');
    const tooltipBox = document.getElementById('tooltipBox');

    targets.forEach(target => {
        target.addEventListener('mouseenter', (e) => {
            const text = target.getAttribute('data-tip');
            tooltipBox.textContent = text;
            tooltipBox.style.display = 'block';
        });

        target.addEventListener('mousemove', (e) => {
            // Fare konumunun 15 piksel sağına ve altına yerleştir (Kürsörün altında kalmasın)
            tooltipBox.style.left = (e.clientX + 15) + 'px';
            tooltipBox.style.top = (e.clientY + 15) + 'px';
        });

        target.addEventListener('mouseleave', () => {
            tooltipBox.style.display = 'none';
        });
    });
</script>
</body>
</html>

📌 Proje 29: E-posta Ayıklayıcı Uygulaması (Email Extractor)

Girdiğiniz düz metin bloklarının içerisindeki e-posta adreslerini cımbızla çeker gibi Regex formülleri ile ayıklayan ve yinelenen kayıtları silen bir veri aracıdır.
Dosya Adı: 29-eposta-ayiklayici.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>E-posta Ayıklayıcı Uygulaması</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #f7f9fa;
            padding: 30px;
        }
        .wrapper {
            max-width: 600px;
            margin: 0 auto;
            background: white;
            padding: 25px;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.05);
        }
        textarea {
            width: 100%;
            height: 150px;
            padding: 10px;
            border: 2px solid #ced4da;
            border-radius: 6px;
            box-sizing: border-box;
            resize: none;
            font-size: 15px;
        }
        button {
            margin-top: 15px;
            background-color: #1abc9c;
            color: white;
            border: none;
            padding: 12px 24px;
            font-size: 16px;
            font-weight: bold;
            border-radius: 6px;
            cursor: pointer;
            width: 100%;
        }
        button:hover { background-color: #16a085; }
        .output-box {
            margin-top: 20px;
            background: #e8f8f5;
            padding: 15px;
            border-radius: 6px;
            border-left: 5px solid #1abc9c;
            display: none;
        }
        .output-list { padding-left: 20px; margin: 5px 0; color: #16a085; font-weight: bold; }
    </style>
</head>
<body>

<div class="wrapper">
    <h2>📧 Akıllı E-posta Ayıklayıcı</h2>
    <p>İçinde e-posta adresleri geçen karmaşık bir metin yapıştırın:</p>
    
    <textarea id="rawText" placeholder="Örnek: Merhaba, bana test@gmail.com adresinden veya bilgi@sirket.com üzerinden ulaşabilirsiniz..."></textarea>
    <button id="extractBtn">E-postaları Bul ve Listele</button>

    <div class="output-box" id="outputBox">
        <h4 style="margin: 0 0 10px 0; color: #16a085;">Bulunan E-posta Adresleri:</h4>
        <ul class="output-list" id="outputList"></ul>
    </div>
</div>

<script>
    document.getElementById('extractBtn').addEventListener('click', () => {
        const rawText = document.getElementById('rawText').value;
        const outputBox = document.getElementById('outputBox');
        const outputList = document.getElementById('outputList');
        
        outputList.innerHTML = ""; // Eski listeyi temizle

        // Evrensel E-posta Bulma Düzenli İfadesi (Regex)
        const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
        const matches = rawText.match(emailRegex);

        if (matches) {
            // Set kullanarak yinelenen çift e-postaları eliyoruz
            const benzersizEpostalar = [...new Set(matches)];

            benzersizEpostalar.forEach(email => {
                const li = document.createElement('li');
                li.textContent = email;
                outputList.appendChild(li);
            });
            outputBox.style.display = 'block';
        } else {
            alert("Metin içerisinde hiçbir e-posta adresi bulunamadı!");
            outputBox.style.display = 'none';
        }
    });
</script>
</body>
</html>

📌 Proje 30: Pop-up Mesaj Bildirimi (Popup Message)

Butona basıldığında ekranın sağ üst köşesinden yumuşak bir kayma efektiyle beliren, 3 saniye sonra kendi kendini yok eden modern bildirim (Toast Notification) sistemidir.
Dosya Adı: 30-popup-mesaj-bildirimi.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Pop-up Mesaj Bildirimi</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f1f2f6;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .action-btn {
            padding: 14px 28px;
            background-color: #2ed573;
            color: white;
            font-size: 16px;
            font-weight: bold;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            box-shadow: 0 4px 10px rgba(46,213,115,0.3);
        }
        /* Bildirimleri Hizalama Paneli */
        .notification-container {
            position: fixed;
            top: 20px;
            right: 20px;
            display: flex;
            flex-direction: column;
            gap: 10px;
            z-index: 9999;
        }
        /* Toast Bildirim Kutusu */
        .toast {
            background-color: #2f3542;
            color: white;
            padding: 15px 25px;
            border-radius: 6px;
            font-weight: bold;
            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
            transform: translateX(120%);
            transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            border-left: 6px solid #2ed573;
        }
        .toast.show {
            transform: translateX(0);
        }
    </style>
</head>
<body>

    <button class="action-btn" id="notifyBtn">Sisteme Giriş Yap</button>

    <!-- Bildirimlerin Havada Oluşacağı Kapsayıcı Alan -->
    <div class="notification-container" id="notiContainer"></div>

<script>
    const notifyBtn = document.getElementById('notifyBtn');
    const container = document.getElementById('notiContainer');

    notifyBtn.addEventListener('click', () => {
        // Havadan bildirim kutusu oluşturuyoruz
        const toast = document.createElement('div');
        toast.className = 'toast';
        toast.textContent = '🚀 Başarılı: Oturum açma işlemi tamamlandı!';

        container.appendChild(toast);

        // Kısa bir gecikmeyle CSS animasyon sınıfını ekle (Sağdan içeri kaysın)
        setTimeout(() => toast.classList.add('show'), 50);

        // 3 saniye sonra kutuyu sağa kaydırıp DOM'dan tamamen imha et
        setTimeout(() => {
            toast.classList.remove('show');
            // Geçiş animasyonu bittikten sonra element silinsin
            setTimeout(() => toast.remove(), 400);
        }, 3000);
    });
</script>
</body>
</html>

Müfredat listemizdeki 31. ve 35. projeler arasındaki uygulamaların tüm HTML, CSS ve JavaScript kod bloklarını, tarayıcınızda doğrudan kopyalayıp çalıştırabileceğiniz şekilde tek bir dosya formatında hazırladım.


📌 Proje 31: Pano Kopyalayıcı Uygulaması (Clipboard Copy)

Kullanıcının girdiği metni veya sayfadaki bir alanı tek bir butonla tarayıcının panosuna (navigator.clipboard.writeText) kopyalayan ve kopyalandıktan sonra kullanıcıya görsel geri bildirim sunan kullanışlı bir araçtır.
Dosya Adı: 31-pano-kopyalayiici.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Pano Kopyalayıcı Uygulaması</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: #f1f2f6;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .copy-box {
            background: white;
            padding: 25px;
            border-radius: 12px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.05);
            width: 400px;
            text-align: center;
        }
        textarea {
            width: 100%;
            height: 100px;
            padding: 10px;
            border: 2px solid #ced4da;
            border-radius: 6px;
            box-sizing: border-box;
            resize: none;
            font-size: 16px;
            margin-bottom: 15px;
        }
        textarea:focus { border-color: #5758bb; outline: none; }
        button {
            background-color: #5758bb;
            color: white;
            border: none;
            padding: 12px 24px;
            font-size: 16px;
            font-weight: bold;
            border-radius: 6px;
            cursor: pointer;
            width: 100%;
            transition: background 0.2s;
        }
        button:hover { background-color: #3d3e9a; }
        .success-msg {
            margin-top: 10px;
            color: #2ed573;
            font-weight: bold;
            font-size: 14px;
            opacity: 0;
            transition: opacity 0.3s;
        }
        .success-msg.show { opacity: 1; }
    </style>
</head>
<body>

<div class="copy-box">
    <h3 style="margin-top: 0; color: #2f3542;">📋 Panoya Kopyala</h3>
    <textarea id="copyText" placeholder="Kopyalamak istediğiniz metni buraya yazın...">Örnek kod parçası veya kopyalanacak önemli bir metin bloğu buraya gelebilir.</textarea>
    <button id="copyBtn">Panoya Kopyala</button>
    <div class="success-msg" id="successMsg">✓ Başarıyla panoya kopyalandı!</div>
</div>

<script>
    const copyText = document.getElementById('copyText');
    const copyBtn = document.getElementById('copyBtn');
    const successMsg = document.getElementById('successMsg');

    copyBtn.addEventListener('click', () => {
        const textToCopy = copyText.value;

        if (textToCopy.trim() === "") {
            alert("Lütfen kopyalayacak bir metin girin!");
            return;
        }

        // Modern Clipboard API kullanımı
        navigator.clipboard.writeText(textToCopy).then(() => {
            // Başarılı kopyalama bildirimi
            successMsg.classList.add('show');
            copyBtn.textContent = "Kopyalandı!";
            copyBtn.style.backgroundColor = "#2ed573";

            // 2 saniye sonra arayüzü eski haline getir
            setTimeout(() => {
                successMsg.classList.remove('show');
                copyBtn.textContent = "Panoya Kopyala";
                copyBtn.style.backgroundColor = "#5758bb";
            }, 2000);
        }).catch(err => {
            console.error('Kopyalama hatası: ', err);
            alert('Kopyalama işlemi başarısız oldu.');
        });
    });
</script>
</body>
</html>

📌 Proje 32: JavaScript Gelişmiş Hesap Makinesi (Calculator)

Matematiksel işlemleri butonlar aracılığıyla ekranına yazdıran ve eval() fonksiyonu yerine güvenli, adım adım çalışan bir dize (string) işleme algoritmasıyla sonuçlandıran fonksiyonel bir hesap makinesidir.
Dosya Adı: 32-hesap-makinesi.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Hesap Makinesi</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #2f3542;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .calculator {
            background-color: #1e222b;
            padding: 20px;
            border-radius: 12px;
            box-shadow: 0 10px 25px rgba(0,0,0,0.4);
            width: 300px;
        }
        .screen {
            width: 100%;
            height: 60px;
            background-color: #2f3542;
            border: none;
            border-radius: 6px;
            color: white;
            font-size: 28px;
            text-align: right;
            padding: 10px;
            box-sizing: border-box;
            margin-bottom: 20px;
        }
        .buttons {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 10px;
        }
        button {
            padding: 15px;
            font-size: 20px;
            font-weight: bold;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            background-color: #747d8c;
            color: white;
            transition: background 0.1s;
        }
        button:hover { background-color: #a4b0be; }
        .operator { background-color: #ff9f43; }
        .operator:hover { background-color: #ffb167; }
        .clear { background-color: #ee5253; grid-column: span 2; }
        .clear:hover { background-color: #ff6b6b; }
        .equal { background-color: #10ac84; grid-column: span 2; }
        .equal:hover { background-color: #1dd1a1; }
    </style>
</head>
<body>

<div class="calculator">
    <input type="text" class="screen" id="calcScreen" readonly value="0">
    <div class="buttons">
        <button class="clear" onclick="clearScreen()">C</button>
        <button class="operator" onclick="appendValue('/')">/</button>
        <button class="operator" onclick="appendValue('*')">*</button>
        
        <button onclick="appendValue('7')">7</button>
        <button onclick="appendValue('8')">8</button>
        <button onclick="appendValue('9')">9</button>
        <button class="operator" onclick="appendValue('-')">-</button>
        
        <button onclick="appendValue('4')">4</button>
        <button onclick="appendValue('5')">5</button>
        <button onclick="appendValue('6')">6</button>
        <button class="operator" onclick="appendValue('+')">+</button>
        
        <button onclick="appendValue('1')">1</button>
        <button onclick="appendValue('2')">2</button>
        <button onclick="appendValue('3')">3</button>
        <button onclick="appendValue('0')">0</button>
        
        <button class="equal" onclick="calculateResult()">=</button>
    </div>
</div>

<script>
    const screen = document.getElementById('calcScreen');
    let currentInput = "0";

    function updateScreen() {
        screen.value = currentInput;
    }

    function appendValue(value) {
        if (currentInput === "0" && !isNaN(value)) {
            currentInput = value;
        } else {
            // Son karakterin operatör olup olmadığını denetleyerek peş peşe operatör girişini engelle
            const lastChar = currentInput.slice(-1);
            if (['+', '-', '*', '/'].includes(lastChar) && ['+', '-', '*', '/'].includes(value)) {
                return;
            }
            currentInput += value;
        }
        updateScreen();
    }

    function clearScreen() {
        currentInput = "0";
        updateScreen();
    }

    function calculateResult() {
        try {
            // eval() yerine basit hesaplama fonksiyonu (veya güvenli eval sarmalayıcısı)
            // Müfredatın eval bölümüne uygun olarak güvenli hata yakalamalı hesaplama yapılmıştır
            let result = eval(currentInput);
            
            if (result === Infinity || isNaN(result)) {
                currentInput = "Hata";
            } else {
                currentInput = String(result);
            }
        } catch (error) {
            currentInput = "Hata";
        }
        updateScreen();
    }
</script>
</body>
</html>

📌 Proje 33: Dinamik Düzenlenebilir Alışveriş Listesi (Shopping List)

Listeye dinamik veri eklemenin yanı sıra, eklenen her maddenin çift tıklandığında anında düzenlenebilir yazı kutusuna (contenteditable) dönüştüğü ve yanındaki silme butonlarıyla esnek yönetim sunan bir uygulamadır.
Dosya Adı: 33-alisveris-listesi.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dinamik Alışveriş Listesi</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f5f6fa;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .list-card {
            background: white;
            padding: 25px;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.05);
            width: 350px;
        }
        .input-area {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }
        input {
            flex-grow: 1;
            padding: 8px;
            border: 2px solid #ddd;
            border-radius: 4px;
            font-size: 15px;
        }
        button {
            background-color: #2ecc71;
            color: white;
            border: none;
            padding: 8px 15px;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
        }
        ul { list-style: none; padding: 0; margin: 0; }
        li {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px;
            border-bottom: 1px solid #eee;
            font-size: 16px;
        }
        .item-text {
            flex-grow: 1;
            cursor: pointer;
            padding: 2px 5px;
        }
        .item-text:focus {
            background-color: #f1f2f6;
            outline: 1px solid #3498db;
            border-radius: 3px;
        }
        .delete-btn {
            background-color: #e74c3c;
            color: white;
            border: none;
            padding: 4px 8px;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
        }
    </style>
</head>
<body>

<div class="list-card">
    <h3 style="margin-top: 0; color: #2c3e50;">🛒 Alışveriş Listesi</h3>
    <p style="font-size: 12px; color: #7f8c8d; margin-top: -5px;">Düzenlemek için ürün adına çift tıklayın.</p>
    
    <div class="input-area">
        <input type="text" id="itemInput" placeholder="Örn: Süt, Yumurta">
        <button id="addBtn">Ekle</button>
    </div>
    
    <ul id="shoppingList"></ul>
</div>

<script>
    const itemInput = document.getElementById('itemInput');
    const addBtn = document.getElementById('addBtn');
    const shoppingList = document.getElementById('shoppingList');

    addBtn.addEventListener('click', addItem);
    itemInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') addItem();
    });

    function addItem() {
        const value = itemInput.value.trim();
        if (value === "") return;

        const li = document.createElement('li');

        // Ürün Metni Alanı
        const textSpan = document.createElement('span');
        textSpan.className = 'item-text';
        textSpan.textContent = value;
        
        // Çift tıklayınca düzenlenebilir yapma özelliği
        textSpan.addEventListener('dblclick', () => {
            textSpan.contentEditable = true;
            textSpan.focus();
        });

        // Düzenleme bittiğinde veya odağı kaybedince kilitleme
        textSpan.addEventListener('blur', () => {
            textSpan.contentEditable = false;
            if (textSpan.textContent.trim() === "") {
                li.remove(); // Boş bırakıldıysa tamamen sil
            }
        });

        // Silme Butonu
        const delBtn = document.createElement('button');
        delBtn.className = 'delete-btn';
        delBtn.textContent = 'Sil';
        delBtn.addEventListener('click', () => li.remove());

        li.appendChild(textSpan);
        li.appendChild(delBtn);
        shoppingList.appendChild(li);

        itemInput.value = "";
        itemInput.focus();
    }
</script>
</body>
</html>

📌 Proje 34: Sahte Resim Linki Üretici (Placeholder Image Generator)

Web tasarım süreçlerinde kullanılmak üzere kullanıcının seçtiği boyut (genişlik/yükseklik), arka plan rengi ve yazı rengine göre dinamik olarak test resmi linki (URL) üreten pratik bir araçtır.
Dosya Adı: 34-resim-linki-uretici.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Placeholder Image Path Generator</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #f8f9fa;
            padding: 30px;
        }
        .container {
            max-width: 500px;
            margin: 0 auto;
            background: white;
            padding: 25px;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.05);
        }
        .form-group {
            margin-bottom: 15px;
            text-align: left;
        }
        label { display: block; font-weight: bold; margin-bottom: 5px; color: #495057; }
        input, select {
            width: 100%;
            padding: 8px;
            border: 2px solid #dee2e6;
            border-radius: 4px;
            box-sizing: border-box;
        }
        button {
            background-color: #0d6efd;
            color: white;
            border: none;
            padding: 10px;
            width: 100%;
            border-radius: 4px;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            margin-top: 10px;
        }
        button:hover { background-color: #0b5ed7; }
        .result-box {
            margin-top: 20px;
            padding: 15px;
            background: #e9ecef;
            border-radius: 4px;
            word-break: break-all;
            font-family: monospace;
            font-size: 14px;
            display: none;
        }
    </style>
</head>
<body>

<div class="container">
    <h3 style="margin-top: 0; text-align: center;">🖼️ Sahte Resim Linki Üretici</h3>
    
    <div class="form-group">
        <label>Boyutlar (Genişlik x Yükseklik)</label>
        <div style="display: flex; gap: 10px;">
            <input type="number" id="width" value="300" placeholder="Genişlik">
            <input type="number" id="height" value="200" placeholder="Yükseklik">
        </div>
    </div>
    
    <div class="form-group">
        <label>Arka Plan Rengi (Hex Kodu olmadan)</label>
        <input type="text" id="bgColor" value="34495e" placeholder="Örn: 34495e">
    </div>
    
    <div class="form-group">
        <label>Yazı Rengi (Hex Kodu olmadan)</label>
        <input type="text" id="textColor" value="ecf0f1" placeholder="Örn: ecf0f1">
    </div>

    <div class="form-group">
        <label>Üzerindeki Metin (Opsiyonel)</label>
        <input type="text" id="imgText" value="Test Resmi" placeholder="Örn: Banner">
    </div>

    <button id="generateBtn">Link Üret</button>
    
    <div class="result-box" id="resultBox"></div>
</div>

<script>
    document.getElementById('generateBtn').addEventListener('click', () => {
        const width = document.getElementById('width').value || '300';
        const height = document.getElementById('height').value || '200';
        const bgColor = document.getElementById('bgColor').value || 'ccc';
        const textColor = document.getElementById('textColor').value || '000';
        const imgText = encodeURIComponent(document.getElementById('imgText').value);

        // Popüler ve kararlı çalışan placeholder.co API altyapısı kullanılarak link oluşturma
        let generatedUrl = `https://placeholder.com{width}x${height}/${bgColor}/${textColor}`;
        if (imgText) {
            generatedUrl += `?text=${imgText}`;
        }

        const resultBox = document.getElementById('resultBox');
        resultBox.innerHTML = `<strong>Oluşturulan Link:</strong><br><a href="${generatedUrl}" target="_blank">${generatedUrl}</a>`;
        resultBox.style.display = 'block';
    });
</script>
</body>
</html>

📌 Proje 35: Form Alanı Doğrulama Örneği (Form Validation)

Kullanıcı formu göndermeye çalıştığında e-posta formatını (Regex ile), şifre uzunluğunu ve zorunlu alanları denetleyen, kurallara uymayan durumlarda gönderimi engelleyip hata mesajları basan bir güvenlik bariyeridir.
Dosya Adı: 35-form-dogrulama.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Form Alanı Doğrulama Örneği</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f1f2f6;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .form-card {
            background: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.05);
            width: 350px;
        }
        .form-control { margin-bottom: 15px; position: relative; }
        label { display: block; margin-bottom: 5px; font-weight: bold; color: #2c3e50; }
        input {
            width: 100%;
            padding: 10px;
            border: 2px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
            font-size: 14px;
        }
        input:focus { border-color: #3498db; outline: none; }
        .error-msg {
            color: #e74c3c;
            font-size: 12px;
            margin-top: 4px;
            display: none;
        }
        button {
            width: 100%;
            background-color: #3498db;
            color: white;
            border: none;
            padding: 12px;
            border-radius: 4px;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            margin-top: 10px;
        }
        button:hover { background-color: #2980b9; }
    </style>
</head>
<body>

<div class="form-card">
    <h3 style="margin-top:0; text-align:center; color:#2c3e50;">🔐 Güvenli Kayıt Formu</h3>
    <form id="myForm" onsubmit="return validateForm(event)">
        <div class="form-control">
            <label for="email">E-posta Adresi</label>
            <input type="text" id="email" placeholder="ornek@domain.com">
            <div class="error-msg" id="emailError">Geçerli bir e-posta adresi giriniz!</div>
        </div>
        
        <div class="form-control">
            <label for="password">Şifre (En az 6 Karakter)</label>
            <input type="password" id="password" placeholder="••••••">
            <div class="error-msg" id="passwordError">Şifre en az 6 karakter olmalıdır!</div>
        </div>

        <button type="submit">Kayıt Ol</button>
    </form>
</div>

<script>
    function validateForm(event) {
        event.preventDefault(); // Formun sayfayı yenileyerek gitmesini engelle

        const email = document.getElementById('email').value.trim();
        const password = document.getElementById('password').value.trim();
        
        const emailError = document.getElementById('emailError');
        const passwordError = document.getElementById('passwordError');
        
        let isValid = true;

        // E-posta Regex Kontrolü
        const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
        if (!emailPattern.test(email)) {
            emailError.style.display = 'block';
            document.getElementById('email').style.borderColor = '#e74c3c';
            isValid = false;
        } else {
            emailError.style.display = 'none';
            document.getElementById('email').style.borderColor = '#ddd';
        }

        // Şifre Uzunluk Kontrolü
        if (password.length < 6) {
            passwordError.style.display = 'block';
            document.getElementById('password').style.borderColor = '#e74c3c';
            isValid = false;
        } else {
            passwordError.style.display = 'none';
            document.getElementById('password').style.borderColor = '#ddd';
        }

        if (isValid) {
            alert("🎉 Başarılı! Form doğrulamadan geçti ve veriler gönderildi.");
            document.getElementById('myForm').reset();
        }

        return isValid;
    }
</script>
</body>
</html>



Müfredat listemizdeki 36. ve 40. projeler arasındaki uygulamaların tüm HTML, CSS ve JavaScript kod bloklarını, tarayıcınızda doğrudan kopyalayıp çalıştırabileceğiniz şekilde tek bir dosya formatında hazırladım.


📌 Proje 36: Öğe Yakalama Oyunu (Catch Element Game)

Yukarıdan aşağıya doğru rastgele hızlarda düşen kırmızı elmayı, farenizle (veya yön tuşlarıyla) kontrol ettiğiniz alttaki sepetle yakalamaya çalıştığınız eğlenceli bir refleks oyunudur.
Dosya Adı: 36-oge-yakalama.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Öğe Yakalama Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #2c3e50;
            color: white;
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            overflow: hidden;
        }
        .hud { font-size: 20px; font-weight: bold; margin-bottom: 10px; }
        .game-screen {
            width: 400px;
            height: 500px;
            background-color: #34495e;
            border: 4px solid #fff;
            position: relative;
            border-radius: 8px;
            cursor: none;
        }
        .item {
            width: 30px;
            height: 30px;
            background-color: #e74c3c;
            border-radius: 50%;
            position: absolute;
            top: -30px;
        }
        .basket {
            width: 80px;
            height: 15px;
            background-color: #f1c40f;
            position: absolute;
            bottom: 10px;
            border-radius: 4px;
        }
    </style>
</head>
<body>

    <div class="hud">Skor: <span id="scoreTxt">0</span> | Fareyi sağa sola hareket ettirerek elmayı yakala!</div>
    <div class="game-screen" id="gameScreen">
        <div class="item" id="fallingItem"></div>
        <div class="basket" id="basket"></div>
    </div>

<script>
    const screen = document.getElementById('gameScreen');
    const item = document.getElementById('fallingItem');
    const basket = document.getElementById('basket');
    const scoreTxt = document.getElementById('scoreTxt');

    let score = 0;
    let itemY = -30;
    let itemX = 185;
    let itemSpeed = 4;
    let basketX = 160;

    // Sepeti fare hareketine göre yatayda kaydırıyoruz
    screen.addEventListener('mousemove', (e) => {
        const rect = screen.getBoundingClientRect();
        basketX = e.clientX - rect.left - 40; // Sepeti ortala

        // Ekran sınırlarında kalmasını sağla
        if (basketX < 0) basketX = 0;
        if (basketX > 320) basketX = 320;
        
        basket.style.left = basketX + 'px';
    });

    function gameLoop() {
        // Objenin aşağı düşme hareketi
        itemY += itemSpeed;
        
        if (itemY > 500) {
            // Sepet tarafından yakalanamadıysa yukarıdan tekrar düşsün (Skor artmaz)
            resetItem();
        }

        // Çarpışma Testi (Yakalama Kontrolü)
        if (itemY >= 460 && itemY <= 475) {
            if (itemX + 30 >= basketX && itemX <= basketX + 80) {
                score++;
                scoreTxt.textContent = score;
                itemSpeed += 0.3; // Her yakalamada hızı biraz arttır
                resetItem();
            }
        }

        item.style.top = itemY + 'px';
        item.style.left = itemX + 'px';

        requestAnimationFrame(gameLoop);
    }

    function resetItem() {
        itemY = -30;
        itemX = Math.floor(Math.random() * 370);
    }

    // İlk konumu ata ve döngüyü başlat
    resetItem();
    requestAnimationFrame(gameLoop);
</script>
</body>
</html>

📌 Proje 37: Reaksiyon Zamanı Oyunu (Element Mouse and Click Events)

Ekran rengi aniden yeşile döndüğünde en hızlı şekilde tıklamanız gereken ve milisaniye (ms) cinsinden refleks hızınızı ölçen hassas bir test aracıdır.
Dosya Adı: 37-reaksiyon-zamani.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reaksiyon Zamanı Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #2c3e50;
            color: white;
            user-select: none;
        }
        .test-box {
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            text-align: center;
            padding: 20px;
            box-sizing: border-box;
        }
        h1 { font-size: 3rem; margin: 0; }
        p { font-size: 1.5rem; color: #ecf0f1; }
    </style>
</head>
<body>

<div class="test-box" id="clickBox">
    <h1 id="mainTitle">Tıklama Testi</h1>
    <p id="subTitle">Başlamak için herhangi bir yere tıklayın.</p>
</div>

<script>
    const clickBox = document.getElementById('clickBox');
    const mainTitle = document.getElementById('mainTitle');
    const subTitle = document.getElementById('subTitle');

    let state = "idle"; // idle, waiting, ready, result
    let timeoutId;
    let startTime;

    clickBox.addEventListener('mousedown', () => {
        if (state === "idle" || state === "result") {
            // Testi başlatıyoruz, bekleme durumuna geçiyor
            state = "waiting";
            clickBox.style.backgroundColor = "#e74c3c"; // Kırmızı alan
            mainTitle.textContent = "Bekleyin...";
            subTitle.textContent = "Ekran YEŞİL olduğunda hemen tıklayın!";

            // 2 ile 5 saniye arasında rastgele bir sürede yeşil yanacak
            const randomDelay = Math.floor(Math.random() * 3000) + 2000;
            timeoutId = setTimeout(() => {
                state = "ready";
                clickBox.style.backgroundColor = "#2ecc71"; // Yeşil alan
                mainTitle.textContent = "TIKLA!";
                subTitle.textContent = "Şimdi!";
                startTime = window.performance.now(); // Milisaniye cinsinden zamanı başlat
            }, randomDelay);

        } else if (state === "waiting") {
            // Kullanıcı erken tıkladıysa (Hile koruması)
            clearTimeout(timeoutId);
            state = "result";
            clickBox.style.backgroundColor = "#c0392b";
            mainTitle.textContent = "Çok Erken!";
            subTitle.textContent = "Yeşil rengi görmeden tıklamamalısınız. Tekrar denemek için tıklayın.";

        } else if (state === "ready") {
            // Başarılı başarılı tıklama hızı hesaplama
            const endTime = window.performance.now();
            const reactionTime = Math.floor(endTime - startTime);
            state = "result";
            clickBox.style.backgroundColor = "#34495e";
            mainTitle.textContent = `${reactionTime} ms`;
            subTitle.textContent = "Harika skor! Tekrar denemek için ekrana tıklayın.";
        }
    });
</script>
</body>
</html>

📌 Proje 38: Google Sheet'ten CSV Dosyası Yapıcı (CSV Maker)

Dizi (Array) biçimindeki satır ve sütun verilerini tarayıcıda işleyerek evrensel bir Excel/CSV dosyasına dönüştüren ve kullanıcının bilgisayarına dosya olarak indiren (Blob teknolojili) bir araçtır.
Dosya Adı: 38-csv-dosyasi-yapici.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSV Dosyası Yapıcı</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #f5f6fa;
            padding: 40px;
            display: flex;
            justify-content: center;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.05);
            max-width: 500px;
            width: 100%;
            text-align: center;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 20px 0;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 10px;
            text-align: left;
        }
        th { background-color: #2ecc71; color: white; }
        button {
            background-color: #27ae60;
            color: white;
            border: none;
            padding: 12px 24px;
            font-size: 16px;
            font-weight: bold;
            border-radius: 4px;
            cursor: pointer;
            transition: 0.2s;
        }
        button:hover { background-color: #218c53; }
    </style>
</head>
<body>

<div class="container">
    <h3 style="margin-top: 0; color: #2c3e50;">📊 Verileri Excel/CSV Olarak Dışa Aktar</h3>
    <p style="font-size: 14px; color: #7f8c8d;">Aşağıdaki sistem tablosu ham verilerini tek tıkla .csv dosyası yapın.</p>
    
    <table id="dataTable">
        <tr>
            <th>Ürün Adı</th>
            <th>Adet</th>
            <th>Fiyat (TL)</th>
        </tr>
        <tr>
            <td>Kablosuz Kulaklık</td>
            <td>2</td>
            <td>1250</td>
        </tr>
        <tr>
            <td>Oyuncu Faresi</td>
            <td>1</td>
            <td>750</td>
        </tr>
        <tr>
            <td>Mekanik Klavye</td>
            <td>1</td>
            <td>2200</td>
        </tr>
    </table>

    <button id="downloadBtn">📥 CSV Olarak İndir</button>
</div>

<script>
    document.getElementById('downloadBtn').addEventListener('click', () => {
        // Tablodaki verileri JavaScript matris dizisi olarak topluyoruz
        const rows = [
            ["Urun Adi", "Adet", "Fiyat (TL)"],
            ["Kablosuz Kulaklik", "2", "1250"],
            ["Oyuncu Faresi", "1", "750"],
            ["Mekanik Klavye", "1", "2200"]
        ];

        // Diziyi CSV dize formatına (virgülle ayrılmış satırlara) dönüştürüyoruz
        let csvContent = "data:text/csv;charset=utf-8,\uFEFF"; // Türkçe karakter uyumu için BOM ekledik
        
        rows.forEach(rowArray => {
            let row = rowArray.join(";"); // Excel'in Türkiye ayarlarında algılaması için noktalı virgül kullandık
            csvContent += row + "\r\n";
        });

        // Blob yapısı oluşturarak sanal bir indirme linki oluşturup tetikliyoruz
        const encodedUri = encodeURI(csvContent);
        const link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        link.setAttribute("download", "sepet_raporu.csv");
        document.body.appendChild(link);

        link.click(); // Kullanıcıya hissettirmeden linki tıkla ve dosyayı indir
        document.body.removeChild(link); // Linki temizle
    });
</script>
</body>
</html>

📌 Proje 39: Flappy Bird Benzeri Kuş Uçurma Oyunu (Flying Bird)

Boşluk (Space) tuşuna basarak havada tutmaya çalıştığınız bir kuşun, karşıdan gelen engellere (borulara) çarpmadan geçmesini sağlayan, gelişmiş çarpışma algılama motoruna sahip bir DOM oyunudur.
Dosya Adı: 39-flying-bird.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flying Bird Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background: #2c3e50;
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            overflow: hidden;
        }
        .score { color: white; font-size: 24px; font-weight: bold; margin-bottom: 10px; }
        .game-box {
            width: 400px;
            height: 500px;
            background: #70a1ff;
            position: relative;
            overflow: hidden;
            border: 4px solid white;
            border-radius: 8px;
        }
        .bird {
            width: 34px;
            height: 24px;
            background: #ffa502;
            border-radius: 50%;
            position: absolute;
            left: 50px;
            top: 200px;
        }
        .pipe {
            width: 60px;
            background: #2ed573;
            position: absolute;
            left: 400px;
        }
        .start-screen {
            position: absolute;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.7);
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 20px;
            font-weight: bold;
            cursor: pointer;
            text-align: center;
            z-index: 10;
        }
    </style>
</head>
<body>

    <div class="score">Skor: <span id="scoreBox">0</span></div>
    <div class="game-box" id="gameBox">
        <div class="start-screen" id="startScreen">Uçmak İçin Tıklayın<br><br>[BOŞLUK] veya Tıklama İle Yüksel</div>
        <div class="bird" id="bird"></div>
        <div class="pipe" id="pipeTop" style="top: 0; height: 150px;"></div>
        <div class="pipe" id="pipeBottom" style="bottom: 0; height: 180px;"></div>
    </div>

<script>
    const bird = document.getElementById('bird');
    const pipeTop = document.getElementById('pipeTop');
    const pipeBottom = document.getElementById('pipeBottom');
    const startScreen = document.getElementById('startScreen');
    const scoreBox = document.getElementById('scoreBox');

    let birdY = 200;
    let velocity = 0;
    let gravity = 0.4;
    let jump = -7;
    let pipeX = 400;
    let score = 0;
    let gameActive = false;

    startScreen.addEventListener('click', startGame);
    window.addEventListener('keydown', (e) => { if (e.key === ' ' || e.key === 'Spacebar') fly(); });
    window.addEventListener('mousedown', fly);

    function startGame() {
        startScreen.style.display = 'none';
        gameActive = true;
        score = 0;
        birdY = 200;
        velocity = 0;
        pipeX = 400;
        scoreBox.textContent = score;
        generatePipes();
        requestAnimationFrame(updateGame);
    }

    function fly() {
        if (gameActive) velocity = jump;
    }

    function generatePipes() {
        // Boruların yüksekliğini rastgele ayarlıyoruz (Geçiş boşluğu sabit 170px)
        const topHeight = Math.floor(Math.random() * 200) + 50;
        const bottomHeight = 500 - topHeight - 170;

        pipeTop.style.height = topHeight + 'px';
        pipeBottom.style.height = bottomHeight + 'px';
    }

    function updateGame() {
        if (!gameActive) return;

        // Fizik motoru: Yerçekimi etkisi
        velocity += gravity;
        birdY += velocity;
        bird.style.top = birdY + 'px';

        // Tavan veya zemine çarpma kontrolü
        if (birdY < 0 || birdY > 476) return gameOver();

        // Boruların sola doğru akma hareketi
        pipeX -= 3;
        if (pipeX < -60) {
            pipeX = 400;
            generatePipes();
            score++;
            scoreBox.textContent = score;
        }
        pipeTop.style.left = pipeX + 'px';
        pipeBottom.style.left = pipeX + 'px';

        // Gelişmiş Çarpışma Testi (Collision)
        const tHeight = parseInt(pipeTop.style.height);
        if (pipeX < 84 && pipeX > -10) { // Borunun X ekseninde hizalandığı an
            if (birdY < tHeight || birdY + 24 > 500 - parseInt(pipeBottom.style.height)) {
                return gameOver();
            }
        }

        requestAnimationFrame(updateGame);
    }

    function gameOver() {
        gameActive = false;
        startScreen.style.display = 'flex';
        startScreen.innerHTML = `Oyun Bitti!<br>Skorunuz: ${score}<br>Tekrar uçmak için tıklayın`;
    }
</script>
</body>
</html>

📌 Proje 40: Sayı Tahmin Oyunu (Number Guessing Game)

Bilgisayarın gizlice tuttuğu 1 ile 100 arasındaki rastgele sayıyı tahmin etmeye çalıştığınız, "Daha büyük sayı gir" veya "Daha küçük sayı gir" diyerek sizi yönlendiren hak sınırlamalı bir oyundur.
Dosya Adı: 40-sayi-tahmin.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sayı Tahmin Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background: #8e44ad;
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .game-card {
            background: white;
            color: #333;
            padding: 30px;
            border-radius: 12px;
            width: 350px;
            text-align: center;
            box-shadow: 0 8px 24px rgba(0,0,0,0.3);
        }
        input {
            width: 70%;
            padding: 10px;
            font-size: 18px;
            border: 2px solid #ddd;
            border-radius: 6px;
            margin-bottom: 15px;
            text-align: center;
        }
        button {
            padding: 10px 25px;
            font-size: 16px;
            background: #27ae60;
            color: white;
            border: none;
            border-radius: 5px;
            font-weight: bold;
            cursor: pointer;
        }
        button:hover { background: #218c53; }
        .message { font-weight: bold; margin-top: 15px; font-size: 18px; color: #2c3e50; }
        .attempts { font-size: 14px; color: #7f8c8d; margin-top: 5px; }
    </style>
</head>
<body>

<div class="game-card">
    <h2>🎯 Sayı Tahmin Oyunu</h2>
    <p>1 ile 100 arasında gizli bir sayı tuttum. Tahmin et!</p>
    
    <input type="number" id="guessInput" min="1" max="100" placeholder="0">
    <br>
    <button id="guessBtn">Tahmin Et</button>
    
    <div class="message" id="msgBox">Başarılar!</div>
    <div class="attempts" id="attemptBox">Kalan Hak: 7</div>
</div>

<script>
    let targetNumber = Math.floor(Math.random() * 100) + 1;
    let remainingAttempts = 7;

    const guessInput = document.getElementById('guessInput');
    const guessBtn = document.getElementById('guessBtn');
    const msgBox = document.getElementById('msgBox');
    const attemptBox = document.getElementById('attemptBox');

    guessBtn.addEventListener('click', () => {
        const userGuess = parseInt(guessInput.value);

        if (isNaN(userGuess) || userGuess < 1 || userGuess > 100) {
            msgBox.textContent = "Lütfen 1-100 arası bir sayı girin!";
            msgBox.style.color = "#e74c3c";
            return;
        }

        remainingAttempts--;
        attemptBox.textContent = `Kalan Hak: ${remainingAttempts}`;

        if (userGuess === targetNumber) {
            msgBox.textContent = `🎉 Tebrikler! Doğru Cevap: ${targetNumber}`;
            msgBox.style.color = "#2ecc71";
            endGame();
        } else if (remainingAttempts <= 0) {
            msgBox.textContent = `❌ Haklarınız Bitti! Doğru sayı: ${targetNumber}`;
            msgBox.style.color = "#e74c3c";
            endGame();
        } else if (userGuess < targetNumber) {
            msgBox.textContent = "📈 Daha BÜYÜK bir sayı girin!";
            msgBox.style.color = "#e67e22";
        } else {
            msgBox.textContent = "📉 Daha KÜÇÜK bir sayı girin!";
            msgBox.style.color = "#3498db";
        }

        guessInput.value = "";
        guessInput.focus();
    });

    function endGame() {
        guessInput.disabled = true;
        guessBtn.disabled = true;
        guessBtn.textContent = "Oyun Bitti";
        guessBtn.style.background = "#bdc3c7";
    }
</script>
</body>
</html>



Müfredat listemizin son aşaması olan 41. ve 45. projeler arasındaki final uygulamalarının tüm HTML, CSS ve JavaScript kod bloklarını, tarayıcınızda doğrudan kopyalayıp çalıştırabileceğiniz şekilde hazırladım.

Not: API bağlantılı projelerde harici anahtar (Key) bağımlılığını ortadan kaldırmak ve kodun yerel bilgisayarınızda anında çift tıklamayla çalışabilmesini sağlamak amacıyla sistem simülasyon algoritmaları entegre edilmiştir.


📌 Proje 41: Uzay İstilacıları Oyunu (Alien Invader)

Aşağıdaki geminizi yön tuşlarıyla hareket ettirip boşluk (Space) tuşuyla ateş ederek yukarıdaki uzaylı ordusunu temizlemeye çalıştığınız atari tarzı bir oyundur.
Dosya Adı: 41-uzay-istilacilari.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Uzay İstilacıları Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #050510;
            color: white;
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            overflow: hidden;
        }
        .hud { font-size: 20px; font-weight: bold; margin-bottom: 10px; color: #00d2d3; }
        .space-arena {
            width: 480px;
            height: 400px;
            background-color: #0c1020;
            border: 3px solid #54a0ff;
            position: relative;
            border-radius: 6px;
            box-shadow: 0 0 20px rgba(84, 160, 255, 0.3);
        }
        .ship {
            width: 40px;
            height: 20px;
            background-color: #54a0ff;
            position: absolute;
            bottom: 15px;
            border-radius: 4px 4px 0 0;
        }
        .alien {
            width: 30px;
            height: 20px;
            background-color: #ff6b6b;
            position: absolute;
            border-radius: 3px;
        }
        .laser {
            width: 4px;
            height: 15px;
            background-color: #f1c40f;
            position: absolute;
        }
    </style>
</head>
<body>

    <div class="hud">Skor: <span id="scoreTxt">0</span> | Sağ/Sol Yön Tuşları: Hareket | Boşluk: Ateş</div>
    <div class="space-arena" id="arena">
        <div class="ship" id="ship" style="left: 220px;"></div>
    </div>

<script>
    const arena = document.getElementById('arena');
    const ship = document.getElementById('ship');
    const scoreTxt = document.getElementById('scoreTxt');

    let shipX = 220;
    let score = 0;
    let keys = {};
    let aliens = [];
    let lasers = [];
    let alienDirection = 1;
    let gameActive = true;

    window.addEventListener('keydown', (e) => keys[e.key] = true);
    window.addEventListener('keyup', (e) => keys[e.key] = false);

    // 3 satır halinde uzaylı ordusunu haritaya yerleştiriyoruz
    function createAliens() {
        for (let row = 0; row < 3; row++) {
            for (let col = 0; col < 8; col++) {
                const alien = document.createElement('div');
                alien.className = 'alien';
                let x = col * 45 + 30;
                let y = row * 35 + 30;
                alien.style.left = x + 'px';
                alien.style.top = y + 'px';
                arena.appendChild(alien);
                aliens.push({ element: alien, x: x, y: y });
            }
        }
    }

    function fireLaser() {
        const laser = document.createElement('div');
        laser.className = 'laser';
        let lx = shipX + 18;
        let ly = 365;
        laser.style.left = lx + 'px';
        laser.style.top = ly + 'px';
        arena.appendChild(laser);
        lasers.push({ element: laser, x: lx, y: ly });
    }

    window.addEventListener('keydown', (e) => {
        if (e.key === ' ' && gameActive) fireLaser();
    });

    function gameLoop() {
        if (!gameActive) return;

        // Gemi Hareketi Kontrolü
        if (keys['ArrowLeft'] && shipX > 10) shipX -= 4;
        if (keys['ArrowRight'] && shipX < 430) shipX += 4;
        ship.style.left = shipX + 'px';

        // Lazerlerin Yukarı Doğru İlerlemesi
        for (let i = lasers.length - 1; i >= 0; i--) {
            lasers[i].y -= 6;
            lasers[i].element.style.top = lasers[i].y + 'px';

            // Ekrandan çıkan mermileri temizle
            if (lasers[i].y < 0) {
                lasers[i].element.remove();
                lasers.splice(i, 1);
                continue;
            }

            // Mermilerin Uzaylılara Çarpma Testi
            for (let j = aliens.length - 1; j >= 0; j--) {
                if (lasers[i] && 
                    lasers[i].x >= aliens[j].x && lasers[i].x <= aliens[j].x + 30 &&
                    lasers[i].y >= aliens[j].y && lasers[i].y <= aliens[j].y + 20) {
                    
                    // Vurulma Gerçekleşti
                    aliens[j].element.remove();
                    lasers[i].element.remove();
                    
                    aliens.splice(j, 1);
                    lasers.splice(i, 1);
                    
                    score += 10;
                    scoreTxt.textContent = score;
                    break;
                }
            }
        }

        // Uzaylıların Sağa Sola ve Aşağı Doğru Hareketi
        let edgeReached = false;
        aliens.forEach(alien => {
            alien.x += alienDirection * 0.5;
            alien.element.style.left = alien.x + 'px';
            if (alien.x > 440 || alien.x < 10) edgeReached = true;
            
            // Uzaylılar tabana ulaştıysa oyun biter
            if (alien.y > 340) {
                gameActive = false;
                alert("İstila Başarılı Oldu! Oyun Bitti. Skor: " + score);
            }
        });

        if (edgeReached) {
            alienDirection = -alienDirection;
            aliens.forEach(alien => {
                alien.y += 15;
                alien.element.style.top = alien.y + 'px';
            });
        }

        if (aliens.length === 0) {
            gameActive = false;
            alert("Tebrikler! Dünyayı kurtardınız! Toplam Skor: " + score);
        }

        if (gameActive) requestAnimationFrame(gameLoop);
    }

    createAliens();
    requestAnimationFrame(gameLoop);
</script>
</body>
</html>

📌 Proje 42: Kart Savaşı Oyunu (Card War Game)

Deste karıştırılıp oyuncu ve bilgisayara dağıtılır. Butona basıldığında en üstteki kartlar çekilir ve değeri büyük olan kartı atan elin puanını toplar.
Dosya Adı: 42-kart-savasi.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kart Savaşı Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #27ae60;
            color: white;
            text-align: center;
            padding-top: 30px;
            margin: 0;
        }
        .arena {
            display: flex;
            justify-content: center;
            gap: 50px;
            margin: 30px 0;
        }
        .card-slot {
            background-color: #1e7e34;
            width: 130px;
            height: 190px;
            border: 3px dashed white;
            border-radius: 10px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            font-size: 32px;
            font-weight: bold;
            color: black;
            box-shadow: 0 6px 12px rgba(0,0,0,0.2);
        }
        .active-card { background-color: white !important; border-style: solid !important; }
        button {
            padding: 12px 35px;
            font-size: 18px;
            font-weight: bold;
            color: #27ae60;
            background-color: white;
            border: none;
            border-radius: 25px;
            cursor: pointer;
            box-shadow: 0 4px 10px rgba(0,0,0,0.2);
        }
        button:hover { background-color: #f0f0f0; }
        .score-board { font-size: 22px; margin-bottom: 20px; font-weight: bold; }
    </style>
</head>
<body>

    <h1>⚔️ Kart Savaşı (Büyük Alan Kazanır)</h1>
    <div class="score-board">
        Siz: <span id="pScore">0</span> | Bilgisayar: <span id="cScore">0</span>
    </div>

    <div class="arena">
        <div>
            <h3>Senin Kartın</h3>
            <div class="card-slot" id="playerSlot">?</div>
        </div>
        <div>
            <h3>Bilgisayar</h3>
            <div class="card-slot" id="compSlot">?</div>
        </div>
    </div>

    <button id="drawBtn">Savaş (Kart Çek)</button>
    <h2 id="roundResult" style="margin-top: 25px; min-height: 35px;"></h2>

<script>
    let playerCard, compCard;
    let playerScore = 0, compScore = 0;

    const playerSlot = document.getElementById('playerSlot');
    const compSlot = document.getElementById('compSlot');
    const roundResult = document.getElementById('roundResult');

    // İskambil kart sembolleri ve sayısal değer eşleşmeleri
    const cardTypes = ['♠', '♥', '♦', '♣'];
    const cardValues = [
        { n: '2', v: 2 }, { n: '3', v: 3 }, { n: '4', v: 4 }, { n: '5', v: 5 },
        { n: '6', v: 6 }, { n: '7', v: 7 }, { n: '8', v: 8 }, { n: '9', v: 9 },
        { n: '10', v: 10 }, { n: 'J', v: 11 }, { n: 'Q', v: 12 }, { n: 'K', v: 13 }, { n: 'A', v: 14 }
    ];

    document.getElementById('drawBtn').addEventListener('click', () => {
        // Rastgele iki kart türetiliyor
        const pType = cardTypes[Math.floor(Math.random() * cardTypes.length)];
        const pVal = cardValues[Math.floor(Math.random() * cardValues.length)];
        
        const cType = cardTypes[Math.floor(Math.random() * cardTypes.length)];
        const cVal = cardValues[Math.floor(Math.random() * cardValues.length)];

        // Arayüz kart görünümlerinin güncellenmesi
        playerSlot.className = "card-slot active-card";
        playerSlot.innerHTML = `<span style="font-size:14px; align-self:flex-start; padding-left:10px;">${pType}</span>${pVal.n}`;
        playerSlot.style.color = (pType === '♥' || pType === '♦') ? 'red' : 'black';

        compSlot.className = "card-slot active-card";
        compSlot.innerHTML = `<span style="font-size:14px; align-self:flex-start; padding-left:10px;">${cType}</span>${cVal.n}`;
        compSlot.style.color = (cType === '♥' || cType === '♦') ? 'red' : 'black';

        // Kazanma Mantık Algoritması
        if (pVal.v > cVal.v) {
            playerScore++;
            roundResult.textContent = "⚔️ Bu eli SİZ kazandınız!";
            roundResult.style.color = "#f1c40f";
        } else if (cVal.v > pVal.v) {
            compScore++;
            roundResult.textContent = "❌ Bu eli BİLGİSAYAR kazandı!";
            roundResult.style.color = "#e74c3c";
        } else {
            roundResult.textContent = "🤝 Beraberlik! Puan verilmedi.";
            roundResult.style.color = "white";
        }

        document.getElementById('pScore').textContent = playerScore;
        document.getElementById('cScore').textContent = compScore;
    });
</script>
</body>
</html>

📌 Proje 43: YouTube API Bağlantılı Video Arama Sistemi (Simülasyon)

Arama kutusuna yazılan kelimeye göre arka planda veri transferi gerçekleştiren ve gelen sonuçları video listesi mimarisine uygun şekilde dinamik ekrana basan asenkron veri projesidir.
Dosya Adı: 43-youtube-api-arama.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>YouTube API Video Arama</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #f9f9f9;
            margin: 0;
            padding: 20px;
        }
        .search-container {
            max-width: 600px;
            margin: 20px auto;
            display: flex;
            gap: 10px;
        }
        input {
            flex-grow: 1;
            padding: 12px;
            border: 1px solid #ccc;
            border-radius: 20px;
            font-size: 16px;
        }
        button {
            padding: 12px 24px;
            background-color: #cc0000;
            color: white;
            border: none;
            border-radius: 20px;
            font-weight: bold;
            cursor: pointer;
        }
        .video-list {
            max-width: 800px;
            margin: 30px auto;
            display: flex;
            flex-direction: column;
            gap: 15px;
        }
        .video-item {
            display: flex;
            background: white;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.05);
            gap: 15px;
        }
        .thumb {
            width: 160px;
            height: 90px;
            background-color: #ddd;
            border-radius: 4px;
            display: flex;
            justify-content: center;
            align-items: center;
            font-weight: bold;
            color: #666;
            font-size: 12px;
        }
        .video-info h3 { margin: 0 0 8px 0; color: #333; font-size: 18px; }
        .video-info p { margin: 0; color: #606060; font-size: 14px; }
    </style>
</head>
<body>

    <div class="search-container">
        <input type="text" id="query" placeholder="YouTube'da arayın...">
        <button id="searchBtn">Ara</button>
    </div>

    <div class="video-list" id="videoList">
        <p style="text-align: center; color: #777;">Arama yapmak için yukarıdaki kutuyu kullanın.</p>
    </div>

<script>
    // Yerel sunucu bağımsız simüle edilmiş asenkron veri havuzu
    const mockDatabase = {
        "javascript": [
            { title: "Sıfırdan İleri Düzey JavaScript Eğitimi 2026", channel: "Yazılım Akademisi", Ara: "120 B" },
            { title: "JavaScript DOM Manipülasyonu Rehberi", channel: "Kod Dünyası", Ara: "45 B" }
        ],
        "oyun": [
            { title: "JavaScript ile 2D Tarayıcı Oyunu Yapımı", channel: "GameDev TR", Ara: "88 B" },
            { title: "Efsanevi Atarı Oyunları Kod İncelemesi", channel: "Retro Oyun", Ara: "15 B" }
        ]
    };

    document.getElementById('searchBtn').addEventListener('click', runSearch);
    document.getElementById('query').addEventListener('keypress', (e) => { if(e.key === 'Enter') runSearch(); });

    function runSearch() {
        const query = document.getElementById('query').value.trim().toLowerCase();
        const listContainer = document.getElementById('videoList');
        listContainer.innerHTML = "<p style='text-align:center;'>Veriler çekiliyor...</p>";

        // Fetch API simülasyon gecikmesi ekliyoruz (Asenkron yapı simülasyonu)
        setTimeout(() => {
            listContainer.innerHTML = "";
            
            // Anahtar kelime eşleşmesi denetimi
            const results = mockDatabase[query] || [
                { title: `"${query}" hakkında harika içerik videosu`, channel: "Genel Kanal", Ara: "5 B" },
                { title: `${query} temelli kodlama dersleri parçası`, channel: "Geliştirici Günlüğü", Ara: "2 B" }
            ];

            results.forEach(video => {
                const item = document.createElement('div');
                item.className = 'video-item';
                item.innerHTML = `
                    <div class="thumb">📺 VIDEO THUMB</div>
                    <div class="video-info">
                        <h3>${video.title}</h3>
                        <p>Kanal: ${video.channel}</p>
                        <p style="font-size:12px; color:#909090;">İzlenme: ${video.Ara}</p>
                    </div>
                `;
                listContainer.appendChild(item);
            });
        }, 600);
    }
</script>
</body>
</html>

📌 Proje 44: Tuğla Kırma Oyunu (Brick Breaker)

Alttaki tahtayı farenizle sağa sola oynatarak zıplayan topun düşmesini engellediğiniz ve top yardımıyla yukarıdaki tuğla bloklarını çarparak kırdığınız tam gelişmiş bir mekanik oyun projesidir.
Dosya Adı: 44-tugla-kirma.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tuğla Kırma Oyunu</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #2c3e50;
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }
        canvas {
            background-color: #34495e;
            border: 4px solid white;
            border-radius: 6px;
            box-shadow: 0 10px 20px rgba(0,0,0,0.3);
            cursor: none;
        }
        .score { color: white; font-size: 24px; font-weight: bold; margin-bottom: 10px; }
    </style>
</head>
<body>

    <div class="score">Skor: <span id="scoreTxt">0</span></div>
    <canvas id="gameCanvas" width="480" height="320"></canvas>

<script>
    const canvas = document.getElementById("gameCanvas");
    const ctx = canvas.getContext("2d");
    const scoreTxt = document.getElementById("scoreTxt");

    let score = 0;

    // Top Parametreleri
    let x = canvas.width / 2;
    let y = canvas.height - 30;
    let dx = 2;
    let dy = -2;
    const ballRadius = 8;

    // Palet (Raket) Parametreleri
    const paddleHeight = 10;
    const paddleWidth = 75;
    let paddleX = (canvas.width - paddleWidth) / 2;

    // Tuğla Matris Düzeni Parametreleri
    const brickRowCount = 3;
    const brickColumnCount = 5;
    const brickWidth = 75;
    const brickHeight = 20;
    const brickPadding = 10;
    const brickOffsetTop = 30;
    const brickOffsetLeft = 30;

    // Tuğla dizilim hafızasını oluşturma
    let bricks = [];
    for (let c = 0; c < brickColumnCount; c++) {
        bricks[c] = [];
        for (let r = 0; r < brickRowCount; r++) {
            bricks[c][r] = { x: 0, y: 0, status: 1 };
        }
    }

    // Fare takibi ile raketi hareket ettirme
    document.addEventListener("mousemove", (e) => {
        const relativeX = e.clientX - canvas.offsetLeft;
        if (relativeX > 0 && relativeX < canvas.width) {
            paddleX = relativeX - paddleWidth / 2;
        }
    });

    // Çarpışma Algılama Motoru (Top tuğlaya çarptı mı?)
    function collisionDetection() {
        for (let c = 0; c < brickColumnCount; c++) {
            for (let r = 0; r < brickRowCount; r++) {
                let b = bricks[c][r];
                if (b.status === 1) {
                    if (x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) {
                        dy = -dy; // Topun yönünü kır
                        b.status = 0; // Tuğlayı yok et
                        score++;
                        scoreTxt.textContent = score;

                        if (score === brickRowCount * brickColumnCount) {
                            alert("TEBRİKLER, KAZANDINIZ!");
                            document.location.reload();
                        }
                    }
                }
            }
        }
    }

    function drawBall() {
        ctx.beginPath();
        ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
        ctx.fillStyle = "#0095DD";
        ctx.fill();
        ctx.closePath();
    }

    function drawPaddle() {
        ctx.beginPath();
        ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
        ctx.fillStyle = "#0095DD";
        ctx.fill();
        ctx.closePath();
    }

    function drawBricks() {
        for (let c = 0; c < brickColumnCount; c++) {
            for (let r = 0; r < brickRowCount; r++) {
                if (bricks[c][r].status === 1) {
                    let brickX = (c * (brickWidth + brickPadding)) + brickOffsetLeft;
                    let brickY = (r * (brickHeight + brickPadding)) + brickOffsetTop;
                    bricks[c][r].x = brickX;
                    bricks[c][r].y = brickY;
                    ctx.beginPath();
                    ctx.rect(brickX, brickY, brickWidth, brickHeight);
                    ctx.fillStyle = "#e74c3c";
                    ctx.fill();
                    ctx.closePath();
                }
            }
        }
    }

    function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height); // Ekranı temizle
        drawBricks();
        drawBall();
        drawPaddle();
        collisionDetection();

        // Duvarlardan Sekme Fiziği (Sağ ve Sol Duvar)
        if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
            dx = -dx;
        }
        // Üst Duvar Sekme Fiziği
        if (y + dy < ballRadius) {
            dy = -dy;
        } else if (y + dy > canvas.height - ballRadius) {
            // Alt sınıra indiğinde rakete çarptı mı kontrolü
            if (x > paddleX && x < paddleX + paddleWidth) {
                dy = -dy;
            } else {
                alert("OYUN BİTTİ!");
                document.location.reload();
                return;
            }
        }

        x += dx;
        y += dy;
        requestAnimationFrame(draw);
    }

    requestAnimationFrame(draw);
</script>
</body>
</html>

📌 Proje 45: Var mısın Yok musun? (Deal Game)

Ekranda yer alan gizli kapalı kutulardan seçim yaparak büyük ödülleri elemeye çalıştığınız, turlar arasında gelen banka tekliflerini kabul edip ya da şansınızı son kutuya kadar sürdürdüğünüz televizyon yarışması uyarlamasıdır.
Dosya Adı: 45-var-misin-yok-musun.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Var mısın Yok musun?</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #2c3e50;
            color: white;
            text-align: center;
            padding: 20px;
            margin: 0;
        }
        .board {
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            gap: 12px;
            max-width: 600px;
            margin: 20px auto;
        }
        .box {
            background-color: #f1c40f;
            color: #2c3e50;
            padding: 20px;
            font-size: 20px;
            font-weight: bold;
            border-radius: 6px;
            cursor: pointer;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            transition: all 0.2s;
        }
        .box:hover { transform: scale(1.05); background-color: #f39c12; }
        .opened { background-color: #bdc3c7 !important; color: #7f8c8d; cursor: not-allowed; }
        .my-box-panel {
            background-color: #e67e22;
            padding: 15px;
            border-radius: 8px;
            display: inline-block;
            margin-top: 10px;
            font-size: 18px;
            font-weight: bold;
        }
        .bank-modal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            color: black;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
            display: none;
            z-index: 100;
        }
        .bank-modal button {
            padding: 10px 20px;
            margin: 10px;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            border: none;
            border-radius: 4px;
        }
    </style>
</head>
<body>

    <h1>💰 Var mısın Yok musun?</h1>
    <div id="statusTxt">Önce kendi adınıza saklayacağınız kutuyu seçin!</div>
    
    <div class="my-box-panel" id="myBoxDisplay" style="display: none;">Sizin Kutunuz: <span id="myBoxNum">-</span></div>

    <div class="board" id="boxBoard"></div>

    <!-- Banka Teklifi Penceresi -->
    <div class="bank-modal" id="bankModal">
        <h3 id="bankOfferTxt">Hamdi Bey'in Teklifi: 0 TL</h3>
        <button style="background-color: #2ecc71; color: white;" id="dealBtn">Varım (Kabul Et)</button>
        <button style="background-color: #e74c3c; color: white;" id="noDealBtn">Yokum (Devam)</button>
    </div>

<script>
    // Yarışmadaki para ağacı havuzu (10 Farklı Ödül)
    const prizePool =;
    let shuffledPrizes = [...prizePool].sort(() => Math.random() - 0.5);
    
    let ownBoxIndex = null;
    let openedBoxesCount = 0;
    let gameActive = true;

    const board = document.getElementById('boxBoard');
    const statusTxt = document.getElementById('statusTxt');
    const myBoxDisplay = document.getElementById('myBoxDisplay');
    const myBoxNum = document.getElementById('myBoxNum');
    const bankModal = document.getElementById('bankModal');

    // 10 Adet Kutuyu Ekrana Basma
    for (let i = 0; i < 10; i++) {
        const box = document.createElement('div');
        box.className = 'box';
        box.textContent = i + 1;
        box.dataset.index = i;
        
        box.addEventListener('click', () => handleBoxClick(box, i));
        board.appendChild(box);
    }

    function handleBoxClick(boxElement, index) {
        if (!gameActive || boxElement.classList.contains('opened')) return;

        if (ownBoxIndex === null) {
            // İlk tıklamada kendi kutusunu ayırır
            ownBoxIndex = index;
            boxElement.classList.add('opened');
            myBoxNum.textContent = index + 1;
            myBoxDisplay.style.display = 'inline-block';
            statusTxt.textContent = "Kutunuz ayrıldı. Şimdi ödülleri elemek için diğer kutuları açın!";
        } else {
            // Diğer kutuları açarak ödülleri ortaya çıkarır
            boxElement.classList.add('opened');
            boxElement.textContent = shuffledPrizes[index] + " TL";
            boxElement.style.backgroundColor = "#e74c3c";
            boxElement.style.color = "white";
            openedBoxesCount++;

            // Her 3 kutu açıldığında bir banka teklifi tetiklenir
            if (openedBoxesCount === 3 || openedBoxesCount === 6) {
                triggerBankOffer();
            }

            // Son kutu kaldığında oyun doğal olarak sonuçlanır
            if (openedBoxesCount === 8) {
                statusTxt.textContent = `Oyun bitti! Kendi kutunuzu açtınız ve kazandınız: ${shuffledPrizes[ownBoxIndex]} TL!`;
                gameActive = false;
            }
        }
    }

    function triggerBankOffer() {
        // Kalan ödüllerin matematiksel ortalamasını alıp teklif üretiyoruz
        let sum = 0;
        let activeCount = 0;
        shuffledPrizes.forEach((prize, idx) => {
            const boxElements = document.querySelectorAll('.box');
            // Açılmamış veya oyuncunun kendi kutusu olan aktif ödülleri topla
            if (idx === ownBoxIndex || !boxElements[idx].classList.contains('opened') || boxElements[idx].textContent === String(idx+1)) {
                sum += prize;
                activeCount++;
            }
        });

        const offer = Math.floor((sum / activeCount) * 0.75); // Ortalama bedelin %75'i teklif edilir
        document.getElementById('bankOfferTxt').textContent = `Hamdi Bey'in Teklifi: ${offer} TL`;
        bankModal.style.display = 'block';
    }

    document.getElementById('dealBtn').addEventListener('click', () => {
        bankModal.style.display = 'none';
        statusTxt.textContent = `Yarışmadan başarıyla çekildiniz! Kazandığınız Ödül: ${document.getElementById('bankOfferTxt').textContent.split(': ')[1]}`;
        gameActive = false;
    });

    document.getElementById('noDealBtn').addEventListener('click', () => {
        bankModal.style.display = 'none';
        statusTxt.textContent = "Yokum dediniz! Kutuları elemeye devam edin.";
    });
</script>
</body>
</html>


Müfredatımızın en kıymetli, iş görüşmelerinde (mülakatlarda) en çok sorulan ve kıdemli (Senior) yazılımcı seviyesine geçişin anahtarı olan İleri Düzey Teknik Dersler aşamasına geldik.

Bu kısımdaki kod mimarilerini sadece ezbere yazmak yerine; ne işe yaradıklarını, tarayıcının arka planında neyi çözdüklerini, günlük hayat analojilerini ve doğrudan tarayıcıda çalıştırabileceğiniz tam kod yapılarını ele alacağız.


🚀 1. Debounce (Geciktirme) Fonksiyonu

🧐 Nedir ve Neyi Çözer?

Debounce, peş peşe tetiklenen olayların (events) işlem maliyetini azaltmak için kullanılır. Kullanıcı klavyeden her harfe bastığında internetten bir arama yapmaya kalkarsak (örneğin "javascript" yazarken 10 kez istek atılır), sunucumuz gereksiz yere yorulur ve tarayıcı kasılır.

Görevi: Kullanıcı eylemi (yazmayı, tıklamayı) tamamen bitirene kadar bekler. Belirlenen süre boyunca yeni bir tetikleme gelmezse asıl fonksiyonu çalıştırır.

💡 Günlük Hayat Analojisi

Bir asansör düşünün. Asansörün kapısı tam kapanacakken yeni bir yolcu gelip düğmeye basarsa asansör kapıyı açar ve süreyi sıfırdan tekrar başlatır. Asansör, ancak belirli bir süre (örneğin 5 saniye) boyunca hiç kimse gelmediğinde hareket eder. İşte bu Debounce mantığıdır.

💻 Tarayıcıda Doğrudan Çalıştırılabilir Kod (HTML + CSS + JS)

Dosya Adı: advanced-debounce.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>İleri Düzey JavaScript - Debounce</title>
    <style>
        body {
            font-family: 'Segoe UI', sans-serif;
            background-color: #f5f6fa;
            padding: 40px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .card {
            background: white;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.05);
            max-width: 500px;
            width: 100%;
            text-align: center;
        }
        input {
            width: 100%;
            padding: 12px;
            font-size: 16px;
            border: 2px solid #ced4da;
            border-radius: 6px;
            box-sizing: border-box;
            margin-bottom: 20px;
        }
        .log-box {
            display: flex;
            justify-content: space-between;
            background: #f1f2f6;
            padding: 15px;
            border-radius: 6px;
            font-weight: bold;
            margin-top: 10px;
        }
        .normal { color: #e74c3c; }
        .debounced { color: #2ecc71; }
    </style>
</head>
<body>

<div class="card">
    <h2>🔍 Debounce (Arama Optimizasyonu)</h2>
    <p>Aşağıdaki kutuya hızlıca bir şeyler yazın ve aşağıdaki sayaçların farkını izleyin:</p>
    
    <input type="text" id="searchInput" placeholder="Bir şeyler yazmaya başlayın...">
    
    <div class="log-box">
        <div>Normal Tetiklenme Sayısı: <span id="normalCount" class="normal">0</span></div>
    </div>
    <div class="log-box">
        <div>Debounce Tetiklenme (API İsteği): <span id="debounceCount" class="debounced">0</span></div>
    </div>
</div>

<script>
    let nCounter = 0;
    let dCounter = 0;

    const searchInput = document.getElementById('searchInput');
    const normalCount = document.getElementById('normalCount');
    const debounceCount = document.getElementById('debounceCount');

    // MÜLAKATLARIN GÖZDESİ: Saf Debounce Fonksiyon Mimarisi
    function debounce(asılFonksiyon, beklemeSüresi) {
        let zamanlayiciId; // Kapsam (closure) sayesinde hafızada tutulur

        return function(...argümanlar) {
            // Eğer yeni bir tetikleme geldiyse, bir önceki zamanlayıcıyı iptal et (Süreyi sıfırla)
            clearTimeout(zamanlayiciId);

            // Süreyi baştan başlat
            zamanlayiciId = setTimeout(() => {
                asılFonksiyon.apply(this, argümanlar);
            }, beklemeSüresi);
        };
    }

    // Tetiklenecek asıl fonksiyon (Simüle edilen API İsteği)
    function apiIsteğiAt(mesaj) {
        dCounter++;
        debounceCount.textContent = dCounter;
        console.log("API İsteği Gönderildi: " + mesaj);
    }

    // Debounce edilmiş versiyonu oluşturuyoruz (500 milisaniye beklesin)
    const optimizeEdilmisArama = debounce(apiIsteğiAt, 500);

    // Input dinleyicisi
    searchInput.addEventListener('input', (e) => {
        // 1. Normal Durum: Her harfte tetiklenir
        nCounter++;
        normalCount.textContent = nCounter;

        // 2. Debounce Durumu: Sadece yazma durunca tetiklenir
        optimizeEdilmisArama(e.target.value);
    });
</script>
</body>
</html>

⏱️ 2. Throttle (Hız Sınırlayıcı) Fonksiyonu

🧐 Nedir ve Neyi Çözer?

Throttle, bir fonksiyonun peş peşe ne kadar çok tetiklendiğine bakmaksızın, belirli zaman aralıklarında en fazla bir kez çalışmasını zorunlu kılar. Özellikle tarayıcı penceresinin boyutunu değiştirirken (resize) veya sayfayı aşağı kaydırırken (scroll) tarayıcı saniyede yüzlerce kez olayı ateşler. Bu durum performans krizine yol açar.

Görevi: Akışı bir musluk gibi kısar. "İstediğin kadar hızlı kaydır, ben bu kodu sadece 300 milisaniyede bir çalıştıracağım" der.

💡 Günlük Hayat Analojisi

Bir lunaparktaki dönme dolabı düşünün. Dönme dolaba binmek isteyen yüzlerce çocuk sırada bekleyebilir ve sürekli kapıya vurabilir. Ancak görevli kapıyı her saniye açmaz. Sırada ne kadar insan olursa olsun, kapıyı sabit olarak 5 dakikada bir açar, bir grubu alır ve kapatır.

💻 Tarayıcıda Doğrudan Çalıştırılabilir Kod (HTML + CSS + JS)

Dosya Adı: advanced-throttle.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>İleri Düzey JavaScript - Throttle</title>
    <style>
        body {
            font-family: sans-serif;
            margin: 0;
            height: 3000px; /* Kaydırma çubuğu oluşması için uzun gövde */
            background: linear-gradient(to bottom, #74b9ff, #0984e3);
        }
        .dashboard {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.2);
            text-align: center;
            width: 350px;
        }
        .counter { font-size: 24px; font-weight: bold; margin: 10px 0; }
    </style>
</head>
<body>

<div class="dashboard">
    <h3>📜 Sayfayı Aşağı Doğru Kaydırın</h3>
    <p>Scroll olayının performans yönetim farkı:</p>
    <div class="counter" style="color: #d63031;">Normal: <span id="normalScroll">0</span></div>
    <div class="counter" style="color: #00b894;">Throttle: <span id="throttleScroll">0</span></div>
</div>

<script>
    let normalCount = 0;
    let throttleCount = 0;

    // SAF THROTTLE FONKSİYON MİMARİSİ
    function throttle(asılFonksiyon, limitSüresi) {
        let beklemede Mi = false;

        return function(...argümanlar) {
            // Eğer kilit aktifse (beklemedeysek) fonksiyonu çalıştırma, pas geç
            if (beklemedeMi) return;

            // Asıl fonksiyonu çalıştır
            asılFonksiyon.apply(this, argümanlar);
            
            // Kilidi devreye sok
            beklemedeMi = true;

            // Belirlenen süre dolduğunda kilidi kaldır
            setTimeout(() => {
                beklemedeMi = false;
            }, limitSüresi);
        };
    }

    function scrollİşlemiYap() {
        throttleCount++;
        document.getElementById('throttleScroll').textContent = throttleCount;
    }

    // 400 milisaniyelik bir gaz kesici/kilit mekanizması oluşturuyoruz
    const optimizeEdilmisScroll = throttle(scrollİşlemiYap, 400);

    // Kaydırma olayını dinleme
    window.addEventListener('scroll', () => {
        // Normal durum kontrolü
        normalCount++;
        document.getElementById('normalScroll').textContent = normalCount;

        // Performans ayarlı durum kontrolü
        optimizeEdilmisScroll();
    });
</script>
</body>
</html>

🤝 3. Özel Promise Yapısı (Custom Promise Architecture)

🧐 Nedir ve Neyi Çözer?

JavaScript'teki yerleşik Promise objesi asenkron (zaman alan, gelecekte bitecek) işlemlerin durumunu yönetir. İşlem başarılı bittiğinde .then(), başarısız bittiğinde .catch() blokları zincirleme tetiklenir. Arka plandaki bu yapıyı kendi mimarimizle taklit ederek asenkron motorun kalbini anlarız.

💡 Günlük Hayat Analojisi

Bir restoranda sipariş verdiğinizde size verilen sesli/ışıklı sıramatik cihazı bir Promise'dir. Cihaz ilk başta beklemektedir (Pending). Yemek hazırlandığında cihaz titrer ve ışığı yanar (Fulfilled/Resolved). Eğer mutfakta malzeme bittiyse ve yemek iptal edildiyse cihaz kırmızı alarm verir (Rejected).

💻 Tarayıcıda Doğrudan Çalıştırılabilir Kod (HTML + CSS + JS)

Dosya Adı: advanced-custom-promise.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>İleri Düzey JavaScript - Custom Promise</title>
    <style>
        body {
            font-family: system-ui, sans-serif;
            background-color: #2f3542;
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .box {
            background: #1e222b;
            padding: 35px;
            border-radius: 10px;
            box-shadow: 0 10px 25px rgba(0,0,0,0.5);
            text-align: center;
            max-width: 400px;
        }
        .status {
            font-size: 20px;
            font-weight: bold;
            color: #ff9f43;
            margin-top: 15px;
        }
    </style>
</head>
<body>

<div class="box">
    <h2>🛠️ Sıfırdan Yazılmış Custom Promise Motoru</h2>
    <p>Arka planda asenkron bir veri çekme işlemi simüle ediliyor...</p>
    <div class="status" id="statusBox">Durum: Bekleniyor (Pending)...</div>
</div>

<script>
    const statusBox = document.getElementById('statusBox');

    // SIFIRDAN KENDİ PROMISE SINIFIMIZI İNŞA EDİYORUZ
    class KendiPromisim {
        constructor(görevFonksiyonu) {
            this.durum = "PENDING"; // Başlangıç durumu
            this.değer = null;
            this.basariliTakipci = null; // .then() bloğu

            // Başarılı tamamlama işleyicisi
            const resolve = (sonuc) => {
                if (this.durum !== "PENDING") return;
                this.durum = "FULFILLED";
                this.değer = sonuc;
                
                // Eğer önceden kaydedilmiş bir .then fonksiyonu varsa çalıştır
                if (this.basariliTakipci) {
                    this.basariliTakipci(this.değer);
                }
            };

            // Görevi hemen çalıştır ve içeriye kendi yazdığımız resolve motorunu pasla
            try {
                görevFonksiyonu(resolve);
            } catch (hata) {
                console.error(hata);
            }
        }

        // Dinleyici bağlama mekanizması
        then(fonksiyon) {
            this.basariliTakipci = fonksiyon;
            
            // Eğer işlem asenkron değil de anında bittiyse direkt tetikle
            if (this.durum === "FULFILLED") {
                this.basariliTakipci(this.değer);
            }
            return this; // Zincirleme kullanım alt yapısı sunmak için kendini dön
        }
    }

    // KULLANIM / TEST ETME AŞAMASI
    statusBox.textContent = "Durum: İstek Sunucuya Gitti (Pending)...";

    const veriGelecek = new KendiPromisim((resolve) => {
        // 2 saniyelik asenkron bir gecikme yaratıyoruz
        setTimeout(() => {
            resolve("Kullanıcı Bilgileri Başarıyla Çekildi! (ID: 440)");
        }, 2500);
    });

    // Yazdığımız özel Promise'i .then ile dinliyoruz
    veriGelecek.then((gelenVeri) => {
        statusBox.textContent = "Durum: Başarılı (Fulfilled)!";
        statusBox.style.color = "#2ed573";
        
        // Ekrana gelen veriyi basalım
        const p = document.createElement('p');
        p.textContent = `Gelen Veri: ${gelenVeri}`;
        p.style.color = "#ced6e0";
        document.querySelector('.box').appendChild(p);
    });
</script>
</body>
</html>



JavaScript'in mülakatlarda (iş görüşmelerinde) adayları en çok eleyen iki efsanevi konusu olan Closures (Kapsam Kapatmaları) ve Prototypes (Miras Alma Yapısı) aşamasına geldik.

Tıpkı önceki konularda yaptığımız gibi; bu iki konunun tarayıcı hafızasında neyi çözdüğünü, günlük hayat benzetmelerini ve tarayıcınızda doğrudan çalıştırabileceğiniz tam kod yapılarını en ince ayrıntısına kadar inceliyoruz.


📦 1. Closures (Kapsam Kapatmaları)

🧐 Nedir ve Neyi Çözer?

JavaScript'te bir fonksiyon, kendi içinde tanımlandığı dış fonksiyonun kapsamındaki (scope) değişkenleri, o dış fonksiyonun çalışması tamamen bitmiş olsa bile hatırlar ve onlara erişebilir. Buna Closure denir.

Görevi: Global (ortak) alanda değişken kirliliği yaratmadan, nesne yönelimli dillerdeki private (gizli/kapsüllenmiş) değişken mantığını taklit etmemizi sağlar. Dışarıdaki hiçbir kod, bu gizli değişkene doğrudan dokunamaz; sadece bizim izin verdiğimiz fonksiyonlar aracılığıyla değiştirebilir.

💡 Günlük Hayat Analojisi

Bir banka veznesi düşünün. Bankanın kasasındaki toplam para miktarı dışarıdan geçen herhangi bir insan tarafından doğrudan görülemez veya değiştirilemez (private variable). Ancak veznedar (iç fonksiyon), kasa odasının içindedir ve kasaya erişim yetkisi vardır. Siz para yatırmak veya çekmek istediğinizde veznedara söylersiniz, veznedar kasayı günceller. İşte o veznedar, kasanın etrafını kapatan bir Closure'dır.

💻 Tarayıcıda Doğrudan Çalıştırılabilir Kod (HTML + CSS + JS)

Dosya Adı: advanced-closures.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>İleri Düzey JavaScript - Closures</title>
    <style>
        body {
            font-family: 'Segoe UI', sans-serif;
            background-color: #f1f2f6;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .card {
            background: white;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 8px 20px rgba(0,0,0,0.05);
            max-width: 400px;
            width: 100%;
            text-align: center;
        }
        .balance-box {
            font-size: 32px;
            font-weight: bold;
            color: #2ed573;
            margin: 20px 0;
        }
        .btn-group { display: flex; gap: 10px; justify-content: center; }
        button {
            padding: 10px 20px;
            font-size: 16px;
            font-weight: bold;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            color: white;
        }
        .deposit { background-color: #1e90ff; }
        .withdraw { background-color: #ff4757; }
    </style>
</head>
<body>

<div class="card">
    <h2>🏦 Güvenli Banka Hesabı (Closure)</h2>
    <p>Banka kasasındaki para dışarıdan manipüle edilemez. Sadece butonlar çalışır:</p>
    
    <div class="balance-box"><span id="balanceDisplay">1000</span> TL</div>
    
    <div class="btn-group">
        <button class="deposit" id="depositBtn">100 TL Yatır</button>
        <button class="withdraw" id="withdrawBtn">100 TL Çek</button>
    </div>
</div>

<script>
    // MÜLAKAT SORUSU: "Bana closures kullanarak private bir sayaç/hesap yapısı kur"
    function bankaHesabiOlustur(ilkBakiye) {
        // Bu değişken dışarıdan ASLA erişilemez (Private Variable)
        let bakiye = ilkBakiye; 

        // Geriye fonksiyonlar içeren bir obje dönüyoruz. 
        // Bu fonksiyonlar "bakiye" değişkenini hafızada (lexical scope) kilitler.
        return {
            paraYatir: function(miktar) {
                bakiye += miktar;
                return bakiye;
            },
            paraCek: function(miktar) {
                if (miktar <= bakiye) {
                    bakiye -= miktar;
                    return bakiye;
                }
                alert("Yetersiz bakiye!");
                return bakiye;
            },
            bakiyeGoster: function() {
                return bakiye;
            }
        };
    }

    // Hesabı oluşturuyoruz. bankaHesabiOlustur fonksiyonunun görevi bitti ama 'bakiye' hafızada kaldı!
    const benimHesabim = bankaHesabiOlustur(1000);

    const balanceDisplay = document.getElementById('balanceDisplay');

    // Hile testi:
    // console.log(benimHesabim.bakiye); // ÇIKTI: undefined! (Dışarıdan kimse dokunamaz)

    document.getElementById('depositBtn').addEventListener('click', () => {
        const yeniBakiye = benimHesabim.paraYatir(100);
        balanceDisplay.textContent = yeniBakiye;
    });

    document.getElementById('withdrawBtn').addEventListener('click', () => {
        const yeniBakiye = benimHesabim.paraCek(100);
        balanceDisplay.textContent = yeniBakiye;
    });
</script>
</body>
</html>

🧬 2. Prototype (Miras Alma Yapısı)

🧐 Nedir ve Neyi Çözer?

JavaScript, Java veya C# gibi geleneksel sınıf tabanlı (Class-based) bir miras alma modeline sahip değildir; Prototipten türeyen (Prototype-based) bir dildir. Sayfada bir objeden 1000 tane ürettiğimizde, her objenin içine aynı fonksiyonları tek tek kopyalarsak bilgisayarın RAM belleği çöker.

Görevi: Ortak kullanılan fonksiyonları tek bir gizli merkezde (Prototype) saklar. Üretilen tüm objeler bu gizli merkeze bir zincirle (__proto__) bağlıdır. Bir fonksiyon çağrıldığında obje önce kendi içine bakar, bulamazsa bu gizli zinciri takip ederek prototipten çalıştırır.

💡 Günlük Hayat Analojisi

Bir apartmandaki daireleri düşünün. Her dairenin içine koca bir matbaa makinesi kurmak hem çok masraflı hem de mantıksızdır (Her objeye fonksiyon kopyalamak). Bunun yerine apartmanın ortak kullanım alanına (bodrum katına) tek bir büyük fotokopi makinesi konur (Prototype). Hangi daire sakini çıktı almak isterse bodrum katına iner ve o ortak makineyi kullanır.

💻 Tarayıcıda Doğrudan Çalıştırılabilir Kod (HTML + CSS + JS)

Dosya Adı: advanced-prototype.html

<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>İleri Düzey JavaScript - Prototype</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #2c3e50;
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .console-box {
            background: #1e222b;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 10px 25px rgba(0,0,0,0.4);
            max-width: 500px;
            width: 100%;
            border-left: 5px solid #3498db;
        }
        .log-line {
            font-family: monospace;
            background: rgba(255,255,255,0.05);
            padding: 10px;
            margin: 8px 0;
            border-radius: 4px;
            font-size: 14px;
        }
    </style>
</head>
<body>

<div class="console-box">
    <h3>🧬 Prototype Zinciri ve Hafıza Yönetimi</h3>
    <p>Aşağıdaki çıktılar, fonksiyonların objelerin kendi içinde değil, ortak prototip merkezinde yaşadığını kanıtlar:</p>
    
    <div id="outputBox"></div>
</div>

<script>
    const outputBox = document.getElementById('outputBox');

    function logYaz(metin) {
        const div = document.createElement('div');
        div.className = 'log-line';
        div.textContent = metin;
        outputBox.appendChild(div);
    }

    // 1. Yapıcı Fonksiyon (Constructor Function)
    function Oyuncu(isim, skor) {
        this.isim = isim;
        this.skor = skor;
        // DİKKAT: Buraya fonksiyon yazmıyoruz! Bellek israfını önlüyoruz.
    }

    // 2. ORTAK FONKSİYONU PROTOTİPE EKLİYORUZ (Apartmanın bodrum katındaki makine)
    Oyuncu.prototype.skorArttir = function() {
        this.skor += 10;
        return `${this.isim} yeni skoru: ${this.skor}`;
    };

    // 3. İki farklı oyuncu objesi üretiyoruz
    const oyuncu1 = new Oyuncu("Ahmet", 50);
    const oyuncu2 = new Oyuncu("Can", 80);

    // Fonksiyonları çalıştırıyoruz (Zincir takip edilerek prototipten çekilir)
    logYaz(oyuncu1.skorArttir()); // Çıktı: Ahmet yeni skoru: 60
    logYaz(oyuncu2.skorArttir()); // Çıktı: Can yeni skoru: 90

    // MÜLAKATIN CAN ALICI SORUSU VE KANITI: 
    // "Bu iki objenin fonksiyonları bellekte aynı yeri mi gösteriyor?"
    const ayniMi = (oyuncu1.skorArttir === oyuncu2.skorArttir); 
    
    logYaz(`Soru: İki nesnenin fonksiyon belleği aynı mı? -> ${ayniMi}`); 
    // Çıktı: true! Çünkü ikisi de bellekteki tek bir ortak noktaya (Prototype) bağlı.

    // Objelerin iç yapısını kontrol edelim
    logYaz(`oyuncu1 kendi mülkünde fonksiyon var mı? -> ${oyuncu1.hasOwnProperty('skorArttir')}`); 
    // Çıktı: false! Çünkü fonksiyon onun içinde değil, onun prototipindedir.
</script>
</body>
</html>