JavaScript ile Etkileşim Ekleme

Ilya Grigorik
Ilya Grigorik

Yayınlanma tarihi: 31 Aralık 2013

JavaScript, sayfanın neredeyse her yönünü (içerik, stil ve kullanıcı etkileşimine verdiği yanıt) değiştirmemize olanak tanır. Ancak JavaScript, DOM oluşturmayı da engelleyebilir ve sayfanın oluşturulmasını geciktirebilir. En iyi performansı sunmak için JavaScript'inizi eşzamansız hale getirin ve kritik oluşturma yolundan gereksiz JavaScript'leri kaldırın.

Özet

  • JavaScript, DOM ve CSSOM'u sorgulayabilir ve değiştirebilir.
  • CSSOM'daki JavaScript yürütme blokları.
  • JavaScript, açıkça async olarak tanımlanmadığı sürece DOM oluşturmayı engeller.

JavaScript, tarayıcıda çalışan ve sayfanın davranışının neredeyse her yönünü değiştirmemize olanak tanıyan dinamik bir dildir: DOM ağacına öğe ekleyerek ve kaldırarak içeriği değiştirebilir, her öğenin CSSOM özelliklerini değiştirebilir, kullanıcı girişini işleyebilir ve daha pek çok işlem yapabiliriz. Bunu açıklamak için önceki "Merhaba Dünya" örneğinde kısa bir satır içi komut dosyası eklendiğinde ne olduğuna bakın:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

Deneyin

  • JavaScript, DOM'a ulaşmamıza ve gizli span düğümüne referans çekmemize olanak tanır. Düğüm, oluşturma ağacında görünmeyebilir ancak DOM'da kalır. Ardından, referans elde ettiğimizde metnini (.textContent aracılığıyla) değiştirebilir ve hatta hesaplanmış görüntüleme stili özelliğini "none" yerine "inline" olarak geçersiz kılabiliriz. Sayfamızda artık "Merhaba etkileşimli öğrenciler!" ifadesi gösteriliyor.

  • JavaScript, DOM'da yeni öğeler oluşturmamıza, bunlara stil uygulamamıza, eklememize ve kaldırmamıza da olanak tanır. Teknik olarak, sayfamızın tamamı, öğeleri tek tek oluşturup biçimlendiren tek bir büyük JavaScript dosyası olabilir. Bu yöntem işe yarasa da pratikte HTML ve CSS kullanmak çok daha kolaydır. JavaScript işlevimizin ikinci bölümünde yeni bir div öğesi oluşturur, metin içeriğini ayarlar, öğeye stil uygular ve öğeyi body öğesine ekleriz.

Mobil cihazda oluşturulan bir sayfanın önizlemesi.

Böylece, mevcut bir DOM düğümünün içeriğini ve CSS stilini değiştirdik ve belgeye tamamen yeni bir düğüm ekledik. Sayfamız tasarım ödülü kazanmayacaktır ancak JavaScript'in bize sunduğu gücü ve esnekliği göstermektedir.

Ancak JavaScript bize çok fazla güç sağlarken sayfanın nasıl ve ne zaman oluşturulacağıyla ilgili birçok ek sınırlama oluşturur.

Öncelikle, önceki örnekte satır içi komut dosyamızın sayfanın alt kısmına yakın olduğunu fark edin. Neden? Bunu kendiniz deneyebilirsiniz. Ancak komut dosyasını <span> öğesinin üzerine taşırsak komut dosyasının başarısız olduğunu ve dokümanda <span> öğesine referans bulamadığından şikayet ettiğini görürsünüz. Yani getElementsByTagName('span'), null değerini döndürür. Bu, önemli bir özelliği gösterir: Komut dosyamız, dokümana tam olarak yerleştirildiği noktada yürütülür. HTML ayrıştırıcı bir komut dosyası etiketiyle karşılaştığında DOM oluşturma işlemini duraklatır ve kontrolü JavaScript motoruna verir. JavaScript motoru çalışmayı bitirdikten sonra tarayıcı kaldığı yerden devam eder ve DOM oluşturmaya devam eder.

Diğer bir deyişle, komut dosyası bloğumuz henüz işlenmediği için sayfanın sonraki bölümlerindeki öğeleri bulamıyor. Başka bir deyişle: Satır içi komut dosyamızın yürütülmesi DOM oluşturmayı engeller ve ilk oluşturma işlemini de geciktirir.

Sayfamıza komut dosyaları eklemenin bir diğer ince özelliği, komut dosyalarının yalnızca DOM'u değil, CSSOM özelliklerini de okuyup değiştirebilmesidir. Aslında, örnekte span öğesinin display özelliğini none yerine inline olarak değiştirdiğimizde tam olarak bunu yapıyoruz. Peki nasıl bir sonuç alacaksınız? Artık bir yarışma durumu var.

Komut dosyamızı çalıştırmak istediğimizde tarayıcı CSSOM'u indirip oluşturmayı tamamlamamışsa ne olur? Yanıt, performans açısından pek iyi değil: Tarayıcı, CSSOM'u indirip oluşturmayı tamamlayana kadar komut dosyası yürütme ve DOM oluşturma işlemlerini geciktirir.

Özetle, JavaScript; DOM, CSSOM ve JavaScript yürütme arasında birçok yeni bağımlılık sunar. Bu durum, tarayıcının sayfayı işleme ve ekranda oluşturma konusunda önemli gecikmelere neden olabilir:

  • Komut dosyasının dokümanda bulunduğu konum önemlidir.
  • Tarayıcı bir komut dosyası etiketiyle karşılaştığında, komut dosyası yürütülmeyi tamamlayana kadar DOM oluşturma duraklatılır.
  • JavaScript, DOM ve CSSOM'u sorgulayabilir ve değiştirebilir.
  • CSSOM hazır olana kadar JavaScript yürütme duraklatılır.

"Kritik oluşturma yolunu optimize etme", büyük ölçüde HTML, CSS ve JavaScript arasındaki bağımlılık grafiğini anlama ve optimize etme anlamına gelir.

Ayrıştırıcı engelleme ve eşzamansız JavaScript

Varsayılan olarak, JavaScript yürütme "ayrıştırıcıyı engelleme" şeklindedir: Tarayıcı, belgede bir komut dosyasıyla karşılaştığında DOM oluşturma işlemini duraklatmalı, kontrolü JavaScript çalışma zamanına devretmeli ve DOM oluşturma işlemine devam etmeden önce komut dosyasının yürütülmesine izin vermelidir. Bunu önceki örneğimizde satır içi komut dosyasıyla görmüştük. Aslında, satır içi komut dosyaları, yürütmelerini ertelemek için ek kod yazmadığınız sürece her zaman ayrıştırıcıyı engeller.

Komut dosyası etiketi kullanılarak eklenen komut dosyaları ne olacak? Önceki örneği ele alalım ve kodu ayrı bir dosyaya ayıklayalım:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script External</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

app.js

var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

Deneyin

<script> etiketi veya satır içi JavaScript snippet'i kullanıp kullanmadığımıza bakılmaksızın, her ikisinin de aynı şekilde davranmasını beklersiniz. Her iki durumda da tarayıcı, dokümanın geri kalanını işleyemeden önce komut dosyasını duraklatıp yürütür. Ancak harici bir JavaScript dosyası söz konusu olduğunda, komut dosyasının diskten, önbellekten veya uzak bir sunucudan getirilmesini beklemek için tarayıcı duraklatılmalıdır. Bu da kritik oluşturma yoluna on ila binlerce milisaniyelik gecikme ekleyebilir.

Varsayılan olarak tüm JavaScript'ler ayrıştırıcı tarafından engellenir. Tarayıcı, komut dosyasının sayfada ne yapmayı planladığını bilmediği için en kötü durum senaryosunun geçerli olduğunu varsayar ve ayrıştırıcıyı engeller. Tarayıcıya, komut dosyasının tam olarak referans verildiği noktada yürütülmesi gerekmediğine dair bir sinyal gönderildiğinde tarayıcı, DOM'u oluşturmaya devam eder ve komut dosyası hazır olduğunda (ör. dosya önbellekten veya uzak bir sunucudan getirildikten sonra) komut dosyasının yürütülmesine izin verir.

Bunu yapmak için async özelliği <script> öğesine eklenir:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script Async</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Deneyin

Komut dosyası etiketine async anahtar kelimesini eklemek, tarayıcıya komut dosyasının kullanılabilir hale gelmesini beklerken DOM oluşturmayı engellememesini söyler. Bu da performansı önemli ölçüde artırabilir.

Geri bildirim