{{quote {author: "Friedrich Nietzsche", title: "İyinin ve Kötünün Ötesinde", chapter: true}
Çok kötü! Aynı eski hikaye! Evinizi inşa etmeyi bitirdikten sonra, başlamadan önce gerçekten bilmeniz gereken bir şeyi yanlışlıkla öğrendiğinizi fark edersiniz.
quote}}
{{figure {url: "img/chapter_picture_14.jpg", alt: "Dallarında harfler, resimler ve dişliler asılı bir ağacı gösteren illüstrasyon", chapter: "framed"}}}
{{index drawing, parsing}}
Bir web sayfası açtığınızda, tarayıcınız sayfanın ((HTML)) metnini alır ve bunu, bölüm ? içindeki ayrıştırıcımızın programları ayrıştırdığı gibi ayrıştırır. Tarayıcı, belgenin ((yapısı))nın bir modelini oluşturur ve bu modeli sayfayı ekranda çizmek için kullanır.
{{index "live data structure"}}
Bu ((belgenin)) temsili, JavaScript programının ((sandbox)) içinde kullanabileceği oyuncaklardan biridir. Okuyabileceğiniz veya değiştirebileceğiniz bir ((veri yapısı))dır. Bu, canlı bir veri yapısı olarak işlev görür: değiştirildiğinde, ekrandaki sayfa değişiklikleri yansıtacak şekilde güncellenir.
{{index [HTML, structure]}}
Bir HTML belgesini, iç içe geçmiş bir dizi ((kutu)) olarak hayal edebilirsiniz. <body>
ve </body>
gibi etiketler, diğer ((etiket))leri kapsar, bu etiketler de sırasıyla başka etiketler veya ((metin)) içerir. İşte önceki bölümdeki örnek belge:
<!doctype html>
<html>
<head>
<title>My home page</title>
</head>
<body>
<h1>My home page</h1>
<p>Hello, I am Marijn and this is my home page.</p>
<p>I also wrote a book! Read it
<a href="http://eloquentjavascript.net">here</a>.</p>
</body>
</html>
This page has the following structure:
{{figure {url: "img/html-boxes.svg", alt: "Bir HTML belgesini iç içe geçmiş kutular kümesi olarak gösteren diyagram. Dış kutu 'html' olarak etiketlenmiştir ve 'head' ve 'body' olarak etiketlenmiş iki kutu içerir. Bunların içinde başka kutular vardır ve en içteki kutulardan bazıları belgenin metnini içerir.", width: "7cm"}}}
{{indexsee "Document Object Model", DOM}}
Tarayıcının belgeyi temsil etmek için kullandığı veri yapısı bu şekli takip eder. Her kutu için, hangi HTML etiketini temsil ettiğini ve içerdiği kutu ve metinleri öğrenmek için etkileşime girebileceğimiz bir nesne vardır. Bu temsile Document Object Model veya kısaca ((DOM)) denir.
{{index "documentElement property", "head property", "body property", "html (HTML tag)", "body (HTML tag)", "head (HTML tag)"}}
Küresel document
bağı, bu nesnelere erişim sağlar. documentElement
özelliği, <html>
etiketini temsil eden nesneye atıfta bulunur. Her HTML belgesinde bir baş ve bir gövde bulunduğundan, head
ve body
özelliklerine de sahiptir, bu öğelere işaret eder.
{{index [nesting, "of objects"]}}
Bir an için bölüm ? ((sözdizim ağacı))na geri dönün. Yapıları, bir tarayıcının belgesinin yapısına çarpıcı biçimde benzer. Her bir ((düğüm)), başka düğümlere, yani çocuklar, atıfta bulunabilir, bu da kendi çocuklarına sahip olabilir. Bu şekil, öğelerin kendilerine benzer alt öğeler içerebileceği iç içe geçmiş yapılara özgüdür.
{{index "documentElement property", [DOM, tree]}}
Bir veri yapısına, dallanma yapısına sahip olduğunda, ((döngü))süz (bir düğüm doğrudan veya dolaylı olarak kendisini içeremez) ve tek, iyi tanımlanmış bir ((kök)) olduğunda ((ağaç)) denir. DOM durumunda, document.documentElement
kök olarak hizmet eder.
{{index sorting, ["data structure", "tree"], "syntax tree"}}
Ağaçlar bilgisayar biliminde sıkça karşımıza çıkar. HTML belgeleri veya programlar gibi özyinelemeli yapıları temsil etmenin yanı sıra, genellikle sıralı ((küme))ler veri tutmak için kullanılırlar çünkü ağaçlarda öğeler düz bir dizidekinden daha verimli bir şekilde bulunabilir veya eklenebilir.
{{index "leaf node", "Egg language"}}
Tipik bir ağaç, farklı türde ((düğüm))lere sahiptir. Egg dilinin sözdizim ağacında tanımlayıcılar, değerler ve uygulama düğümleri vardı. Uygulama düğümleri çocuklara sahip olabilirken, tanımlayıcılar ve değerler yaprak düğümleri, yani çocuksuz düğümlerdi.
{{index "body property", [HTML, structure]}}
Aynı durum DOM için de geçerlidir. HTML etiketlerini temsil eden ((öğe)) düğümleri, belgenin yapısını belirler. Bu düğümler ((çocuk düğüm))lere sahip olabilir. Bu tür bir düğüme örnek olarak document.body
verilebilir. Bu çocuklardan bazıları ((yaprak düğüm))ler olabilir, örneğin ((metin)) parçaları veya ((yorum)) düğümleri gibi.
{{index "text node", element, "ELEMENT_NODE code", "COMMENT_NODE code", "TEXT_NODE code", "nodeType property"}}
Her DOM düğüm nesnesinin, düğüm türünü tanımlayan bir kod (sayı) içeren bir nodeType
özelliği vardır. Öğeler, sabit özellik Node.ELEMENT_NODE
olarak tanımlanan 1 koduna sahiptir. Belgede bir metin bölümünü temsil eden metin düğümleri 3 kodunu (Node.TEXT_NODE
) alır. Yorumlar 8 koduna (Node.COMMENT_NODE
) sahiptir.
Belgemizi ((ağacı)) görselleştirmenin bir başka yolu da aşağıdaki gibidir:
{{figure {url: "img/html-tree.svg", alt: "HTML belgesini üst düğümlerden alt düğümlere giden oklarla bir ağaç olarak gösteren diyagram", width: "8cm"}}}
Yapraklar metin düğümleridir ve oklar düğümler arasındaki ebeveyn-çocuk ilişkilerini gösterir.
{{id standard}}
{{index "programming language", [interface, design], [DOM, interface]}}
Düğüm türlerini temsil etmek için şifreli sayısal kodlar kullanmak pek JavaScript tarzı bir yaklaşım değildir. Bu bölümün ilerleyen kısımlarında, DOM arayüzünün diğer bölümlerinin de sıkıcı ve yabancı hissettirdiğini göreceğiz. Bunun nedeni, DOM arayüzünün sadece JavaScript için tasarlanmamış olmasıdır. Bunun yerine, sadece HTML için değil, aynı zamanda HTML benzeri bir söz dizimine sahip genel bir ((veri formatı)) olan ((XML)) gibi diğer sistemlerde de kullanılabilecek dil tarafsız bir arayüz olmaya çalışır.
{{index consistency, integration}}
Bu talihsiz bir durum. Standartlar genellikle yararlıdır. Ancak bu durumda, avantaj (diller arası tutarlılık) pek çekici değil. Kullandığınız dil ile düzgün bir şekilde entegre edilmiş bir arayüze sahip olmak, diller arası tanıdık bir arayüze sahip olmaktan daha fazla zaman kazandıracaktır.
{{index "array-like object", "NodeList type"}}
Bu kötü entegrasyona bir örnek olarak, DOM’daki öğe düğümlerinin sahip olduğu childNodes
özelliğini düşünün. Bu özellik, bir length
özelliği olan ve çocuk düğümlere erişmek için numaralarla etiketlenmiş özellikler içeren dizi benzeri bir nesne tutar. Ancak bu, gerçek bir dizi olmayan NodeList
türünün bir örneğidir, bu yüzden slice
ve map
gibi metodlara sahip değildir.
{{index [interface, design], [DOM, construction], "side effect"}}
Daha sonra, basitçe kötü tasarım olan sorunlar var. Örneğin, yeni bir düğüm oluşturup hemen çocukları veya ((özellik))leri eklemenin bir yolu yoktur. Bunun yerine, önce düğümü oluşturmalı ve ardından çocukları ve özellikleri tek tek yan etkiler kullanarak eklemelisiniz. DOM ile yoğun etkileşimde bulunan kodlar genellikle uzun, tekrarlayıcı ve çirkin olur.
{{index library}}
Ancak bu kusurlar ölümcül değil. JavaScript, kendi ((soyutlama))larımızı oluşturmamıza izin verdiği için, gerçekleştirdiğiniz işlemleri ifade etmenin daha iyi yollarını tasarlamak mümkündür. Tarayıcı programlaması için tasarlanmış birçok kütüphane, bu tür araçlarla birlikte gelir.
{{index pointer}}
DOM düğümleri, yakındaki diğer düğümlere çok sayıda ((bağlantı)) içerir. Aşağıdaki diyagram bunları göstermektedir:
{{figure {url: "img/html-links.svg", alt: "DOM düğümleri arasındaki bağlantıları gösteren diyagram. 'body' düğümü, başlangıcındaki 'h1' düğümünü işaret eden bir 'firstChild' oku, son paragraf düğümünü işaret eden bir 'lastChild' oku ve tüm çocuklarına giden bir dizi bağlantıyı işaret eden 'childNodes' oku ile bir kutu olarak gösterilir. Ortadaki paragrafın kendisinden önceki düğümü gösteren bir 'previousSibling' oku, kendisinden sonraki düğümü gösteren bir 'nextSibling' oku ve 'body' düğümünü gösteren bir 'parentNode' oku vardır.”, width: ”6cm"}}}
{{index "child node", "parentNode property", "childNodes property"}}
Diyagram sadece her türden bir bağlantıyı gösterse de, her düğümün, eğer varsa, bir parçası olduğu düğümü işaret eden bir parentNode
özelliği vardır. Aynı şekilde, her öğe düğümü (düğüm türü 1) çocuklarını tutan dizi benzeri bir nesneyi işaret eden bir childNodes
özelliğine sahiptir.
{{index "firstChild property", "lastChild property", "previousSibling property", "nextSibling property"}}
Teoride, sadece bu ana ve çocuk bağlantıları kullanarak ağaçta herhangi bir yere hareket edebilirsiniz. Ancak JavaScript, size bir dizi ek kolaylık bağlantısı da sunar. firstChild
ve lastChild
özellikleri, ilk ve son çocuk öğeleri işaret eder veya çocuğu olmayan düğümler için null
değerine sahiptir. Benzer şekilde, previousSibling
ve nextSibling
, aynı ebeveyne sahip ve düğümün hemen öncesinde veya sonrasında görünen bitişik düğümleri işaret eder. İlk çocuk için, previousSibling
null olacaktır ve son çocuk için nextSibling
null olacaktır.
{{index "children property", "text node", element}}
Ayrıca, children
özelliği de vardır, bu childNodes
gibidir ancak sadece öğe (tür 1) çocukları içerir, diğer türde çocuk düğümleri içermez. Bu, metin düğümleriyle ilgilenmediğinizde yararlı olabilir.
{{index "talksAbout function", recursion, [nesting, "of objects"]}}
Bu tür iç içe geçmiş bir veri yapısı ile çalışırken, özyinelemeli fonksiyonlar genellikle faydalıdır. Aşağıdaki fonksiyon, bir belgeyi belirli bir dizeyi içeren ((metin düğümü))ler için tarar ve birini bulduğunda true
döndürür:
{{id talksAbout}}
function talksAbout(node, string) {
if (node.nodeType == Node.ELEMENT_NODE) {
for (let child of node.childNodes) {
if (talksAbout(child, string)) {
return true;
}
}
return false;
} else if (node.nodeType == Node.TEXT_NODE) {
return node.nodeValue.indexOf(string) > -1;
}
}
console.log(talksAbout(document.body, "book"));
// → true
{{index "nodeValue property"}}
Bir metin düğümünün nodeValue
özelliği, temsil ettiği metin dizesini tutar.
{{index [DOM, querying], "body property", "hard-coding", [whitespace, "in HTML"]}}
Bu ((link))leri ebeveynler, çocuklar ve kardeşler arasında gezdirmek genellikle yararlıdır. Ancak belgede belirli bir düğümü bulmak istiyorsak, document.body
adresinden başlayıp sabit bir özellik yolunu izleyerek ona ulaşmak kötü bir fikirdir. Bunu yapmak, programımıza belgenin kesin yapısı hakkında varsayımlar ekler - daha sonra değiştirmek isteyebileceğiniz bir yapı. Bir başka karmaşık faktör de metin düğümlerinin düğümler arasındaki boşluklar için bile oluşturulmasıdır. Örnek belgenin <body>
etiketinin sadece üç alt öğesi (<h1>
ve iki <p>
öğesi) değil, aslında yedi alt öğesi vardır: bu üç öğe, artı onlardan önceki, sonraki ve aralarındaki boşluklar.
{{index "search problem", "href attribute", "getElementsByTagName method"}}
Dolayısıyla, o belgedeki bağlantının href
niteliğini almak istiyorsak, “Belge gövdesinin altıncı çocuğunun ikinci çocuğunu al” gibi bir şey söylemek istemeyiz. “Belgedeki ilk bağlantıyı al” diyebilseydik daha iyi olurdu. Öyle de yapabiliriz.
let link = document.body.getElementsByTagName("a")[0];
console.log(link.href);
{{index "child node"}}
Tüm öğe düğümleri, verilen etiket adına sahip olan ve bu düğümün torunları (doğrudan veya dolaylı çocukları) olan tüm öğeleri toplayan ve bunları bir ((dizi benzeri nesne)) olarak döndüren bir getElementsByTagName
yöntemine sahiptir.
{{index "id attribute", "getElementById method"}}
Belirli bir single düğümü bulmak için, ona bir id
niteliği verebilir ve bunun yerine document.getElementById
kullanabilirsiniz.
<p>My ostrich Gertrude:</p>
<p><img id="gertrude" src="img/ostrich.png"></p>
<script>
let ostrich = document.getElementById("gertrude");
console.log(ostrich.src);
</script>
{{index "getElementsByClassName method", "class attribute"}}
Üçüncü ve benzer bir yöntem olan getElementsByClassName
, getElementsByTagName
gibi, bir öğe düğümünün içeriğini arar ve class
niteliğinde verilen dizeye sahip tüm öğeleri alır.
{{index "side effect", "removeChild method", "appendChild method", "insertBefore method", [DOM, construction], [DOM, modification]}}
DOM veri yapısıyla ilgili neredeyse her şey değiştirilebilir. Belge ağacının şekli, ebeveyn-çocuk ilişkileri değiştirilerek değiştirilebilir. Düğümler, onları mevcut ebeveyn düğümlerinden kaldırmak için bir remove
yöntemine sahiptir. Bir eleman düğümüne bir çocuk düğüm eklemek için, onu çocuk listesinin sonuna koyan appendChild
veya ilk argüman olarak verilen düğümü ikinci argüman olarak verilen düğümden önce ekleyen insertBefore
kullanabiliriz.
<p>One</p>
<p>Two</p>
<p>Three</p>
<script>
let paragraphs = document.body.getElementsByTagName("p");
document.body.insertBefore(paragraphs[2], paragraphs[0]);
</script>
Bir düğüm belgede yalnızca tek bir yerde bulunabilir. Bu nedenle, Three paragrafını One paragrafının önüne eklemek, önce onu belgenin sonundan kaldıracak ve sonra öne ekleyecek ve Three/One/Two ile sonuçlanacaktır. Bir düğümü bir yere ekleyen tüm işlemler, ((yan etki)) olarak, mevcut konumundan (eğer varsa) kaldırılmasına neden olacaktır.
{{index "insertBefore method", "replaceChild method"}}
Bir alt düğümü başka bir düğümle değiştirmek için replaceChild
yöntemi kullanılır. Argüman olarak iki düğüm alır: yeni bir düğüm ve değiştirilecek düğüm. Değiştirilen düğüm, yöntemin çağrıldığı öğenin bir çocuğu olmalıdır. Hem replaceChild
hem de insertBefore
yöntemlerinin ilk argüman olarak new düğümünü beklediğini unutmayın.
{{index "alt attribute", "img (HTML tag)", "createTextNode method"}}
Belgedeki tüm ((image))leri (<img>
etiketleri), alt
niteliklerinde tutulan ve görüntünün alternatif bir metinsel temsilini belirten metinle değiştiren bir kod yazmak istediğimizi varsayalım.
{{index "createTextNode method"}}
Bu sadece resimleri kaldırmayı değil, aynı zamanda onların yerine yeni bir metin düğümü eklemeyi de içerir.
<p>The <img src="img/cat.png" alt="Cat"> in the
<img src="img/hat.png" alt="Hat">.</p>
<p><button onclick="replaceImages()">Replace</button></p>
<script>
function replaceImages() {
let images = document.body.getElementsByTagName("img");
for (let i = images.length - 1; i >= 0; i--) {
let image = images[i];
if (image.alt) {
let text = document.createTextNode(image.alt);
image.parentNode.replaceChild(text, image);
}
}
}
</script>
{{index "text node"}}
Bir dize verildiğinde, createTextNode
bize ekranda görünmesini sağlamak için belgeye ekleyebileceğimiz bir metin düğümü verir.
{{index "live data structure", "getElementsByTagName method", "childNodes property"}}
Görüntülerin üzerinden geçen döngü listenin sonundan başlar. Bu gereklidir çünkü getElementsByTagName
gibi bir yöntem (ya da childNodes
gibi bir özellik) tarafından döndürülen düğüm listesi canlıdır. Yani, belge değiştikçe güncellenir. Eğer en baştan başlasaydık, ilk resmi kaldırmak listenin ilk elemanını kaybetmesine neden olurdu, böylece i
nin 1 olduğu döngü ikinci kez tekrarlandığında, koleksiyonun uzunluğu artık 1 olduğu için dururdu.
{{index "slice method"}}
Canlı bir koleksiyon yerine solid bir düğüm koleksiyonu istiyorsanız, Array.from
komutunu çağırarak koleksiyonu gerçek bir diziye dönüştürebilirsiniz.
let arrayish = {0: "one", 1: "two", length: 2};
let array = Array.from(arrayish);
console.log(array.map(s => s.toUpperCase()));
// → ["ONE", "TWO"]
{{index "createElement method"}}
((eleman)) düğümleri oluşturmak için document.createElement
yöntemini kullanabilirsiniz. Bu yöntem bir etiket adı alır ve verilen türde yeni bir boş düğüm döndürür.
{{index "Popper, Karl", [DOM, construction], "elt function"}}
{{id elt}}
Aşağıdaki örnek, bir element düğümü oluşturan ve argümanlarının geri kalanını bu düğümün çocukları olarak ele alan bir elt
yardımcı programı tanımlar. Bu fonksiyon daha sonra bir alıntıya atıf eklemek için kullanılır.
<blockquote id="quote">
No book can ever be finished. While working on it we learn
just enough to find it immature the moment we turn away
from it.
</blockquote>
<script>
function elt(type, ...children) {
let node = document.createElement(type);
for (let child of children) {
if (typeof child != "string") node.appendChild(child);
else node.appendChild(document.createTextNode(child));
}
return node;
}
document.getElementById("quote").appendChild(
elt("footer", "—",
elt("strong", "Karl Popper"),
", preface to the second edition of ",
elt("em", "The Open Society and Its Enemies"),
", 1950"));
</script>
{{if book
Ortaya çıkan belge bu şekilde görünür:
{{figure {url: "img/blockquote.png", alt: "Blok alıntının atıfla birlikte işlenmiş resmi", width: "8cm"}}}
if}}
{{index "href attribute", [DOM, attributes]}}
Bazı öğe ((özellik))leri, bağlantılar için href
gibi, öğenin ((DOM)) nesnesindeki aynı isimli bir özellik aracılığıyla erişilebilir. Bu, çoğu yaygın olarak kullanılan standart özellikler için geçerlidir.
{{index "data attribute", "getAttribute method", "setAttribute method", attribute}}
Ancak HTML, düğümler üzerinde istediğiniz herhangi bir özelliği ayarlamanıza izin verir. Bu, bir belgede ek bilgi saklamanızı sağladığı için kullanışlı olabilir. Kendi özellik adlarınızı oluşturduğunuzda, bu tür özellikler öğenin düğümünde özellik olarak bulunmaz. Bunun yerine, onlarla çalışmak için getAttribute
ve setAttribute
metodlarını kullanmanız gerekir.
<p data-classified="secret">The launch code is 00000000.</p>
<p data-classified="unclassified">I have two feet.</p>
<script>
let paras = document.body.getElementsByTagName("p");
for (let para of Array.from(paras)) {
if (para.getAttribute("data-classified") == "secret") {
para.remove();
}
}
</script>
Bu tür oluşturulan özellik adlarının diğer özelliklerle çakışmamasını sağlamak için bu adlara data-
öneki eklemeniz önerilir.
{{index "getAttribute method", "setAttribute method", "className property", "class attribute"}}
JavaScript dilinde bir ((anahtar kelime)) olan class
adında yaygın olarak kullanılan bir özellik vardır. Tarihsel nedenlerden dolayı—bazı eski JavaScript uygulamaları anahtar kelimelerle eşleşen özellik adlarını işleyemiyordu—bu özelliğe erişmek için kullanılan özellik className
olarak adlandırılır. Ayrıca, getAttribute
ve setAttribute
metodlarını kullanarak, gerçek adı olan "class"
altında da erişebilirsiniz.
{{index layout, "block element", "inline element", "p (HTML tag)", "h1 (HTML tag)", "a (HTML tag)", "strong (HTML tag)"}}
Farklı türdeki öğelerin farklı şekillerde yerleştirildiğini fark etmiş olabilirsiniz. Paragraflar (<p>
) veya başlıklar (<h1>
) gibi bazıları belgenin tüm genişliğini kaplar ve ayrı satırlarda görüntülenir. Bunlara block öğeler denir. Diğerleri, bağlantılar (<a>
) veya <strong>
öğesi gibi, çevreleyen metinle aynı satırda görüntülenir. Bu tür öğelere inline öğeler denir.
{{index drawing}}
Herhangi bir belge için, tarayıcılar her öğeye, türüne ve içeriğine göre bir boyut ve konum veren bir düzen hesaplayabilir. Bu düzen daha sonra belgeyi çizmek için kullanılır.
{{index "border (CSS)", "offsetWidth property", "offsetHeight property", "clientWidth property", "clientHeight property", dimensions}}
Bir öğenin boyutu ve konumu JavaScript’ten erişilebilir. offsetWidth
ve offsetHeight
özellikleri, öğenin kapladığı alanı ((piksel)) olarak verir. Bir piksel, tarayıcıda temel ölçü birimidir. Geleneksel olarak, ekranın çizebileceği en küçük nokta ile eşleşir, ancak modern ekranlarda, çok küçük noktalar çizebildiği için bu durum geçerli olmayabilir ve bir tarayıcı pikseli birden fazla ekran noktasını kapsayabilir.
Benzer şekilde, clientWidth
ve clientHeight
özellikleri, kenarlık genişliğini görmezden gelerek öğenin içindeki alanın boyutunu verir.
<p style="border: 3px solid red">
I'm boxed in
</p>
<script>
let para = document.body.getElementsByTagName("p")[0];
console.log("clientHeight:", para.clientHeight);
// → 19
console.log("offsetHeight:", para.offsetHeight);
// → 25
</script>
{{if book
Bir paragrafa kenarlık eklemek, etrafında bir dikdörtgen çizilmesine neden olur.
{{figure {url: "img/boxed-in.png", alt: "Kenarlıklı bir paragrafın çizilmiş resmi", width: "8cm"}}}
if}}
{{index "getBoundingClientRect method", position, "pageXOffset property", "pageYOffset property"}}
{{id boundingRect}}
Bir öğenin ekrandaki kesin konumunu bulmanın en etkili yolu getBoundingClientRect
metodudur. Bu metot, öğenin kenarlarının ekranın sol üst köşesine göre piksel konumlarını belirten top
, bottom
, left
ve right
özelliklerine sahip bir nesne döndürür. Bu konumları tüm belgeye göre almak isterseniz, mevcut kaydırma pozisyonunu eklemeniz gerekir; bu pozisyonu pageXOffset
ve pageYOffset
bağlamlarında bulabilirsiniz.
{{index "offsetHeight property", "getBoundingClientRect method", drawing, laziness, performance, efficiency}}
Bir belgenin düzenini oluşturmak oldukça zahmetli bir iş olabilir. Hız açısından, tarayıcı motorları, bir belgeyi her değiştirdiğinizde hemen yeniden düzenlemez, olabildiğince bekler. Belgeyi değiştiren bir JavaScript programı çalışmayı bitirdiğinde, tarayıcı değiştirilmiş belgeyi ekrana çizmek için yeni bir düzen hesaplamak zorunda kalır. Bir program offsetHeight
gibi özellikleri okuyarak veya getBoundingClientRect
çağırarak bir şeyin konumunu veya boyutunu sorduğunda, doğru bilgi sağlamak da bir ((layout)) hesaplamayı gerektirir.
{{index "side effect", optimization, benchmark}}
DOM düzen bilgilerini okuma ve DOM’u değiştirme arasında sürekli gidip gelen bir program, çok sayıda düzen hesaplaması yapılmasına zorlar ve bu nedenle çok yavaş çalışır. Aşağıdaki kod bunun bir örneğidir. 2.000 piksel genişliğinde X karakterlerinden oluşan bir satır oluşturan ve her birinin ne kadar sürdüğünü ölçen iki farklı program içerir.
<p><span id="one"></span></p>
<p><span id="two"></span></p>
<script>
function time(name, action) {
let start = Date.now(); // Current time in milliseconds
action();
console.log(name, "took", Date.now() - start, "ms");
}
time("naive", () => {
let target = document.getElementById("one");
while (target.offsetWidth < 2000) {
target.appendChild(document.createTextNode("X"));
}
});
// → naive took 32 ms
time("clever", function() {
let target = document.getElementById("two");
target.appendChild(document.createTextNode("XXXXX"));
let total = Math.ceil(2000 / (target.offsetWidth / 5));
target.firstChild.nodeValue = "X".repeat(total);
});
// → clever took 1 ms
</script>
{{index "block element", "inline element", style, "strong (HTML tag)", "a (HTML tag)", underline}}
Farklı HTML öğelerinin farklı şekilde çizildiğini gördük. Bazıları blok olarak, bazıları satır içi olarak görüntülenir. Bazıları stil ekler—<strong>
, içeriğini ((kalın)) yapar ve <a>
, içeriğini mavi yapar ve altını çizer.
{{index "img (HTML tag)", "default behavior", "style attribute"}}
Bir <img>
etiketinin bir görüntüyü nasıl gösterdiği veya bir <a>
etiketinin üzerine tıklandığında bir bağlantının nasıl takip edileceği, öğe türüyle güçlü bir şekilde ilişkilidir. Ancak bir öğe ile ilişkilendirilen stili, örneğin metin rengi veya alt çizgi gibi, değiştirebiliriz. İşte style
özelliğini kullanan bir örnek:
<p><a href=".">Normal link</a></p>
<p><a href="." style="color: green">Green link</a></p>
{{if book
İkinci bağlantı, varsayılan bağlantı renginin yerine yeşil olacak.
{{figure {url: "img/colored-links.png", alt: "Normal mavi bağlantı ve stil verilmiş yeşil bağlantının çizilmiş resmi", width: "2.2cm"}}}
if}}
{{index "border (CSS)", "color (CSS)", CSS, "colon character"}}
Bir stil niteliği bir veya daha fazla ((declaration))s içerebilir; bunlar bir özellik (örneğin color
), ardından iki nokta üst üste ve bir değerdir (örneğin green
). Birden fazla bildirim olduğunda, “color: red; border: none”
gibi ((noktalı virgül))ler ile ayrılmalıdırlar.
{{index "display (CSS)", layout}}
Bir stil niteliği bir veya daha fazla ((declaration))s içerebilir; bunlar bir özellik (örneğin color
), ardından iki nokta üst üste ve bir değerdir (örneğin green
). Birden fazla bildirim olduğunda, “color: red; border: none”
gibi ((noktalı virgül))ler ile ayrılmalıdırlar.
This text is displayed <strong>inline</strong>,
<strong style="display: block">as a block</strong>, and
<strong style="display: none">not at all</strong>.
{{index "hidden element"}}
((Blok öğesi))ler etraflarındaki metinle birlikte satır içinde görüntülenmediğinden block
etiketi kendi satırında sonlanacaktır. Son etiket hiç görüntülenmez-display: none
bir öğenin ekranda görünmesini engeller. Bu, öğeleri gizlemenin bir yoludur. Genellikle belgeden tamamen kaldırmaya tercih edilir çünkü daha sonra tekrar ortaya çıkarmayı kolaylaştırır.
{{if book
{{figure {url: "img/display.png", alt: "Farklı ekran stilleri", width: "4cm"}}}
if}}
{{index "color (CSS)", "style attribute"}}
JavaScript kodu, bir öğenin stilini, öğenin style
özelliği aracılığıyla doğrudan değiştirebilir. Bu özellik, tüm olası stil özellikleri için özelliklere sahip bir nesne tutar. Bu özelliklerin değerleri, öğenin stilinin belirli bir yönünü değiştirmek için yazabileceğimiz dizelerdir.
<p id="para" style="color: purple">
Nice text
</p>
<script>
let para = document.getElementById("para");
console.log(para.style.color);
para.style.color = "magenta";
</script>
{{index "camel case", capitalization, "hyphen character", "font-family (CSS)"}}
Bazı stil özellik adları font-family
gibi kısa çizgiler içerir. Bu tür özellik adlarıyla JavaScript'te çalışmak zor olduğundan (style[“font-family”]
demeniz gerekir), bu tür özellikler için style
nesnesindeki özellik adlarının tire işaretleri kaldırılır ve kendilerinden sonraki harfler büyük yazılır (style.fontFamily
).
{{index "rule (CSS)", "style (HTML tag)"}}
{{indexsee "Cascading Style Sheets", CSS}} {{indexsee "style sheet", CSS}}
HTML için biçimlendirme sistemi, COPYascading Style Sheets için ((CSS)) olarak adlandırılır. Bir style sheet, bir belgedeki öğelerin nasıl biçimlendirileceğine ilişkin bir dizi kuraldır. Bir <style>
etiketi içinde verilebilir.
<style>
strong {
font-style: italic;
color: gray;
}
</style>
<p>Now <strong>strong text</strong> is italic and gray.</p>
{{index "rule (CSS)", "font-weight (CSS)", overlay}}
İsimdeki ((cascading)) kısmı, bir öğenin nihai stilini oluşturmak için birden fazla kuralın birleştirildiği gerçeğine atıfta bulunur. Örnekte, <strong>
etiketleri için varsayılan stil olan font-weight: bold
, font-style
ve color
ekleyen <style>
etiketindeki kuralla birleşir.
{{index "style (HTML tag)", "style attribute"}}
Birden fazla kural aynı özellik için bir değer tanımladığında, en son okunan kural daha yüksek bir ((öncelik)) kazanır ve galip gelir. Dolayısıyla, <style>
etiketindeki kural font-weight: normal
içeriyorsa ve bu, varsayılan font-weight
kuralıyla çelişiyorsa, metin normal olur, kalın değil. Doğrudan düğüme uygulanan bir style
özelliğindeki stiller en yüksek önceliğe sahiptir ve her zaman kazanır.
{{index uniqueness, "class attribute", "id attribute"}}
CSS kurallarında ((etiket)) adları dışında başka şeyleri hedeflemek de mümkündür. .abc
için bir kural, class
özelliğinde "abc"
bulunan tüm öğelere uygulanır. #xyz
için bir kural ise id
özelliği "xyz"
olan öğeye uygulanır (bu özellik belge içinde benzersiz olmalıdır).
.subtle {
color: gray;
font-size: 80%;
}
#header {
background: blue;
color: white;
}
/* p elements with id main and with classes a and b */
p#main.a.b {
margin-bottom: 20px;
}
{{index "rule (CSS)"}}
En son tanımlanan kuralı tercih eden ((öncelik)) kuralı, yalnızca kuralların aynı ((özgüllük)) seviyesine sahip olması durumunda geçerlidir. Bir kuralın özgüllüğü, eşleşen öğeleri ne kadar kesin olarak tanımladığının bir ölçüsüdür ve gerektirdiği öğe özelliklerinin (etiket, sınıf veya ID) sayısı ve türüyle belirlenir. Örneğin, p.a
hedefleyen bir kural, p
veya sadece .a
hedefleyen kurallardan daha özgül olduğu için onlardan öncelikli olur.
{{index "direct child node"}}
p > a {…}
notasyonu, belirtilen stilleri <p>
etiketlerinin doğrudan çocukları olan tüm <a>
etiketlerine uygular. Benzer şekilde, p a {…}
, doğrudan veya dolaylı çocuk olup olmadığına bakılmaksızın, <p>
etiketlerinin içindeki tüm <a>
etiketlerine uygulanır.
{{index complexity, CSS, "domain-specific language", [DOM, querying]}}
Bu kitapta stil sayfalarını çok fazla kullanmayacağız. Tarayıcıda programlama yaparken bunları anlamak yararlıdır, ancak ayrı bir kitap gerektirecek kadar karmaşıktırlar.
{{index "domain-specific language", [DOM, querying]}}
Stil sayfalarında bir stil kümesinin hangi öğelere uygulanacağını belirlemek için kullanılan ((selector)) sözdizimini tanıtmamın ana nedeni, aynı mini dili DOM öğelerini bulmak için etkili bir yol olarak kullanabilmemizdir.
{{index "querySelectorAll method", "NodeList type"}}
Hem document
nesnesi hem de öğe düğümleri üzerinde tanımlanan querySelectorAll
yöntemi, bir seçici dizesi alır ve eşleştiği tüm öğeleri içeren bir NodeList
döndürür.
<p>And if you go chasing
<span class="animal">rabbits</span></p>
<p>And you know you're going to fall</p>
<p>Tell 'em a <span class="character">hookah smoking
<span class="animal">caterpillar</span></span></p>
<p>Has given you the call</p>
<script>
function count(selector) {
return document.querySelectorAll(selector).length;
}
console.log(count("p")); // All <p> elements
// → 4
console.log(count(".animal")); // Class animal
// → 2
console.log(count("p .animal")); // Animal inside of <p>
// → 2
console.log(count("p > .animal")); // Direct child of <p>
// → 1
</script>
{{index "live data structure"}}
getElementsByTagName
gibi yöntemlerin aksine, querySelectorAll
tarafından döndürülen nesne canlı değildir. Belgeyi değiştirdiğinizde değişmez. Yine de gerçek bir dizi değildir, bu nedenle bir dizi gibi davranmak istiyorsanız Array.from
öğesini çağırmanız gerekir.
{{index "querySelector method"}}
querySelector
yöntemi (All
kısmı olmadan) benzer şekilde çalışır. Bu, belirli, tek bir öğe istiyorsanız kullanışlıdır. Yalnızca ilk eşleşen öğeyi veya hiçbir öğe eşleşmediğinde null döndürür.
{{id animation}}
{{index "position (CSS)", "relative positioning", "top (CSS)", "left (CSS)", "absolute positioning"}}
position
stil özelliği düzeni güçlü bir şekilde etkiler. Varsayılan olarak static
değerine sahiptir, yani öğe belgedeki normal yerine oturur. relative
olarak ayarlandığında, öğe belgede hala yer kaplar, ancak artık top
ve left
stil özellikleri onu bu normal yere göre hareket ettirmek için kullanılabilir. position
öğesi absolute
olarak ayarlandığında, öğe normal belge akışından çıkarılır; yani artık yer kaplamaz ve diğer öğelerle çakışabilir. Ayrıca, top
ve left
özellikleri, position
özelliği static
olmayan en yakın çevreleyen öğenin sol üst köşesine göre veya böyle bir çevreleyen öğe yoksa belgeye göre kesinlikle konumlandırmak için kullanılabilir.
{{index [animation, "spinning cat"]}}
Bunu bir animasyon oluşturmak için kullanabiliriz. Aşağıdaki belge, bir ((elips)) içinde hareket eden bir kedi resmi görüntüler:
<p style="text-align: center">
<img src="img/cat.png" style="position: relative">
</p>
<script>
let cat = document.querySelector("img");
let angle = Math.PI / 2;
function animate(time, lastTime) {
if (lastTime != null) {
angle += (time - lastTime) * 0.001;
}
cat.style.top = (Math.sin(angle) * 20) + "px";
cat.style.left = (Math.cos(angle) * 200) + "px";
requestAnimationFrame(newTime => animate(newTime, time));
}
requestAnimationFrame(animate);
</script>
{{if book
Gri ok, görüntünün hareket ettiği yolu gösterir.
{{figure {url: "img/cat-animation.png", alt: "Hareketini gösteren dairesel bir ok ile bir kedi resmini gösteren bir diyagram", width: "8cm"}}}
if}}
{{index "top (CSS)", "left (CSS)", centering, "relative positioning"}}
Resmimiz sayfada ortalanır ve konum
değeri göreceli
olarak verilir. Bu resmi taşımak için resmin top
ve left
stillerini tekrar tekrar güncelleyeceğiz.
{{index "requestAnimationFrame function", drawing, animation}}
{{id animationFrame}}
Kod, animate
fonksiyonunu tarayıcı ekranı yeniden boyamaya hazır olduğunda çalışacak şekilde zamanlamak için requestAnimationFrame
kullanır. Bir sonraki güncellemeyi zamanlamak için animate
fonksiyonunun kendisi yine requestAnimationFrame
fonksiyonunu çağırır. Tarayıcı penceresi (veya sekmesi) aktif olduğunda, bu, güncellemelerin saniyede yaklaşık 60 oranında gerçekleşmesine neden olur ve bu da iyi görünümlü bir animasyon üretme eğilimindedir.
{{index timeline, blocking}}
Eğer DOM'u bir döngü içinde güncelleseydik, sayfa donacak ve ekranda hiçbir şey görünmeyecekti. Tarayıcılar, bir JavaScript programı çalışırken ekranlarını güncellemezler ve sayfayla herhangi bir etkileşime izin vermezler. İşte bu yüzden requestAnimationFrame
e ihtiyacımız var - tarayıcıya şimdilik işimizin bittiğini bildirir ve tarayıcıların yaptığı, ekranı güncellemek ve kullanıcı eylemlerine yanıt vermek gibi şeyleri yapmaya devam edebilir.
{{index "smooth animation"}}
Animasyon fonksiyonuna argüman olarak mevcut ((zaman)) aktarılır. Kedinin milisaniye başına hareketinin sabit olmasını sağlamak için, açının değişme hızını geçerli zaman ile fonksiyonun son çalıştığı zaman arasındaki farka dayandırır. Açıyı adım başına sabit bir miktarda hareket ettirseydi, örneğin aynı bilgisayarda çalışan başka bir ağır görev, işlevin saniyenin bir kısmı boyunca çalışmasını engellerse hareket takılırdı.
{{index "Math.cos function", "Math.sin function", cosine, sine, trigonometry}}
{{id sin_cos}}
((Daire))lerde hareket etmek Math.cos
ve Math.sin
trigonometri fonksiyonları kullanılarak yapılır. Bunlara aşina olmayanlar için, bu kitapta zaman zaman kullanacağımız için kısaca tanıtacağım.
{{index coordinates, pi}}
Math.cos
ve Math.sin
yarıçapı bir olan (0,0) noktası etrafındaki bir çember üzerinde yer alan noktaları bulmak için kullanışlıdır. Her iki fonksiyon da argümanlarını bu daire üzerindeki konum olarak yorumlar, sıfır dairenin en sağındaki noktayı gösterir ve 2π (yaklaşık 6.28) bizi tüm dairenin etrafına götürene kadar saat yönünde ilerler. Math.cos
size verilen konuma karşılık gelen noktanın x koordinatını, Math.sin
ise y koordinatını verir. 2π'den büyük veya 0'dan küçük konumlar (veya açılar) geçerlidir - dönüş tekrar eder, böylece a+2π a ile aynı ((açı)) anlamına gelir.
{{index "PI constant"}}
Açıları ölçmek için kullanılan bu birim ((radyan))s olarak adlandırılır; tam bir daire 2π radyandır, tıpkı derece cinsinden ölçerken 360 derece olması gibi. π sabiti JavaScript'te Math.PI
olarak kullanılabilir.
{{figure {url: "img/cos_sin.svg", alt: "Koordinatları hesaplamak için kosinüs ve sinüs kullanımını gösteren diyagram. Yarıçapı 1 olan bir daire, üzerinde iki nokta ile gösterilmiştir. Dairenin sağ tarafından noktaya olan açı, radyan cinsinden, dairenin merkezinden yatay mesafe için 'cos(açı)' ve dikey mesafe için sin(açı) kullanılarak her bir noktanın konumunu hesaplamak için kullanılır.", width: "6cm"}}}
{{index "counter variable", "Math.sin function", "top (CSS)", "Math.cos function", "left (CSS)", ellipse}}
Kedi animasyonu kodu, animasyonun geçerli açısı için angle
adında bir sayaç tutar ve animate
fonksiyonu her çağrıldığında bunu artırır. Daha sonra görüntü öğesinin geçerli konumunu hesaplamak için bu açıyı kullanabilir. top
stili Math.sin
ile hesaplanır ve elipsimizin dikey yarıçapı olan 20 ile çarpılır. left
stili Math.cos
ile hesaplanır ve elipsin yüksekliğinden çok daha geniş olması için 200 ile çarpılır.
{{index "unit (CSS)"}}
Stillerin genellikle birimlere ihtiyaç duyduğunu unutmayın. Bu durumda, tarayıcıya ((piksel)) cinsinden saydığımızı (santimetre, “ems” veya diğer birimlerin aksine) söylemek için sayıya “px”
eklememiz gerekir. Bunu unutmak kolaydır. Sayıları birimsiz kullanmak, stilinizin göz ardı edilmesine neden olur - sayı 0 olmadığı sürece, bu da birimi ne olursa olsun her zaman aynı anlama gelir.
JavaScript programları, DOM adı verilen bir veri yapısı aracılığıyla tarayıcının görüntülediği belgeyi inceleyebilir ve müdahale edebilir. Bu veri yapısı tarayıcının belge modelini temsil eder ve bir JavaScript programı görünür belgeyi değiştirmek için bu yapıyı değiştirebilir.
DOM, öğelerin belgenin yapısına göre hiyerarşik olarak düzenlendiği bir ağaç gibi organize edilmiştir. Öğeleri temsil eden nesneler, bu ağaçta gezinmek için kullanılabilen parentNode
ve childNodes
gibi özelliklere sahiptir.
Bir belgenin görüntülenme şekli, hem düğümlere doğrudan stil ekleyerek hem de belirli düğümlerle eşleşen kurallar tanımlayarak stillendirme tarafından etkilenebilir. color
veya display
gibi birçok farklı stil özelliği vardır. JavaScript kodu, bir öğenin stilini doğrudan style
özelliği aracılığıyla değiştirebilir.
{{id exercise_table}}
{{index "table (HTML tag)"}}
Bir HTML tablosu aşağıdaki etiket yapısı ile oluşturulur:
<table>
<tr>
<th>name</th>
<th>height</th>
<th>place</th>
</tr>
<tr>
<td>Kilimanjaro</td>
<td>5895</td>
<td>Tanzania</td>
</tr>
</table>
{{index "tr (HTML tag)", "th (HTML tag)", "td (HTML tag)"}}
Her ((row)) için, <table>
etiketi bir <tr>
etiketi içerir. Bu <tr>
etiketlerinin içine hücre öğeleri koyabiliriz: başlık hücreleri (<th>
) veya normal hücreler (<td>
).
Dağlardan oluşan bir veri kümesi, name
, height
ve place
özelliklerine sahip bir dizi nesne verildiğinde, nesneleri numaralandıran bir tablo için DOM yapısını oluşturun. Anahtar başına bir sütun ve nesne başına bir satır, ayrıca en üstte sütun adlarını listeleyen <th>
öğeleri içeren bir başlık satırı olmalıdır.
Bunu, sütunlar verilerdeki ilk nesnenin özellik adlarını alarak nesnelerden otomatik olarak türetilecek şekilde yazın.
Ortaya çıkan tabloyu, id
niteliği “mountains”
olan öğeye ekleyerek belgede gösterin.
{{index "right-aligning", "text-align (CSS)"}}
Bunu çalıştırdıktan sonra, style.textAlign
özelliğini “right”
olarak ayarlayarak sayı değerleri içeren hücreleri sağa hizalayın.
{{if interactive
<h1>Mountains</h1>
<div id="mountains"></div>
<script>
const MOUNTAINS = [
{name: "Kilimanjaro", height: 5895, place: "Tanzania"},
{name: "Everest", height: 8848, place: "Nepal"},
{name: "Mount Fuji", height: 3776, place: "Japan"},
{name: "Vaalserberg", height: 323, place: "Netherlands"},
{name: "Denali", height: 6168, place: "United States"},
{name: "Popocatepetl", height: 5465, place: "Mexico"},
{name: "Mont Blanc", height: 4808, place: "Italy/France"}
];
// Your code here
</script>
if}}
{{hint
{{index "createElement method", "table example", "appendChild method"}}
Yeni eleman düğümleri oluşturmak için document.createElement
, metin düğümleri oluşturmak için document.createTextNode
ve düğümleri diğer düğümlerin içine yerleştirmek için appendChild
yöntemini kullanabilirsiniz.
{{index "Object.keys function"}}
Üst satırı doldurmak için anahtar adları üzerinde bir kez döngü yapmak ve ardından veri satırlarını oluşturmak için dizideki her nesne için tekrar döngü yapmak isteyeceksiniz. İlk nesneden bir dizi anahtar adı almak için Object.keys
yararlı olacaktır.
{{index "getElementById method", "querySelector method"}}
Tabloyu doğru üst düğüme eklemek için, düğümü bulmak üzere document.getElementById
veya document.querySelector
ile “#mountains”
kullanabilirsiniz.
hint}}
{{index "getElementsByTagName method", recursion}}
document.getElementsByTagName
yöntemi, belirli bir etiket adına sahip tüm alt öğeleri döndürür. Bunun kendi versiyonunu, argüman olarak bir düğüm ve bir dize (etiket adı) alan ve verilen etiket adına sahip tüm alt öğe düğümlerini içeren bir dizi döndüren bir fonksiyon olarak uygulayın.
{{index "nodeName property", capitalization, "toLowerCase method", "toUpperCase method"}}
Bir elemanın etiket adını bulmak için nodeName
özelliğini kullanın. Ancak bunun etiket adını büyük harfle döndüreceğini unutmayın. Bunu telafi etmek için toLowerCase
veya toUpperCase
string yöntemlerini kullanın.
{{if interactive
<h1>Heading with a <span>span</span> element.</h1>
<p>A paragraph with <span>one</span>, <span>two</span>
spans.</p>
<script>
function byTagName(node, tagName) {
// Your code here.
}
console.log(byTagName(document.body, "h1").length);
// → 1
console.log(byTagName(document.body, "span").length);
// → 3
let para = document.querySelector("p");
console.log(byTagName(para, "span").length);
// → 2
</script>
if}}
{{hint
{{index "getElementsByTagName method", recursion}}
Çözüm, bu bölümde daha önce tanımlanan talksAbout
fonksiyonuna benzer özyinelemeli bir fonksiyonla en kolay şekilde ifade edilir.
{{index concatenation, "concat method", closure}}
byTagname
fonksiyonunun kendisini özyinelemeli olarak çağırabilir ve elde edilen dizileri birleştirerek çıktıyı üretebilirsiniz. Ya da kendisini özyinelemeli olarak çağıran ve dış fonksiyonda tanımlanan bir dizi bağına erişimi olan ve bulduğu eşleşen elemanları ekleyebileceği bir iç fonksiyon oluşturabilirsiniz. İşlemi başlatmak için dış fonksiyondan bir kez ((iç fonksiyon)) çağırmayı unutmayın.
{{index "nodeType property", "ELEMENT_NODE code"}}
Özyinelemeli fonksiyon düğüm tipini kontrol etmelidir. Burada sadece düğüm tipi 1 (Node.ELEMENT_NODE
) ile ilgileniyoruz. Bu tür düğümler için, çocukları üzerinde döngü yapmalı ve her çocuk için, çocuğun sorguyla eşleşip eşleşmediğine bakmalı ve aynı zamanda kendi çocuklarını incelemek için özyinelemeli bir çağrı yapmalıyız.
hint}}
{{index "cat's hat (exercise)", [animation, "spinning cat"]}}
Daha önce tanımlanan kedi animasyonunu genişletin, böylece hem kedi hem de şapkası (<img src=“img/hat.png”>
) elipsin karşıt taraflarında yörüngeye girsin.
Ya da şapkayı kedinin etrafında döndürün. Ya da animasyonu başka ilginç bir şekilde değiştirin.
{{index "absolute positioning", "top (CSS)", "left (CSS)", "position (CSS)"}}
Birden fazla nesnenin konumlandırılmasını kolaylaştırmak için mutlak konumlandırmaya geçmek muhtemelen iyi bir fikirdir. Bu, top
ve left
değerlerinin belgenin sol üst köşesine göre sayılacağı anlamına gelir. Görüntünün görünür sayfanın dışına çıkmasına neden olacak negatif koordinatlar kullanmaktan kaçınmak için, konum değerlerine sabit sayıda piksel ekleyebilirsiniz.
{{if interactive
<style>body { min-height: 200px }</style>
<img src="img/cat.png" id="cat" style="position: absolute">
<img src="img/hat.png" id="hat" style="position: absolute">
<script>
let cat = document.querySelector("#cat");
let hat = document.querySelector("#hat");
let angle = 0;
let lastTime = null;
function animate(time) {
if (lastTime != null) angle += (time - lastTime) * 0.001;
lastTime = time;
cat.style.top = (Math.sin(angle) * 40 + 40) + "px";
cat.style.left = (Math.cos(angle) * 200 + 230) + "px";
// Your extensions here.
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
</script>
if}}
{{hint
Math.cos
ve Math.sin
açıları radyan cinsinden ölçer, burada tam bir daire 2π'dir. Belirli bir açı için, bunun yarısını ekleyerek karşıt açıyı elde edebilirsiniz, bu da Math.PI
dır. Bu, şapkayı yörüngenin karşı tarafına yerleştirmek için yararlı olabilir.
hint}}