Neden Import?

Web’de farklı kaynakta dosyaları nasıl yüklediğinizi düşünün. Javascript için <script src=""> tagini kullanıyoruz. CSS için ise <link rel="stylesheet" href=""> tagini kullanarak HTML sayfamıza kaynakları ekliyoruz. Bu kaynak eklemeyi resim video ve ses dosyalarını da eklemek olarak çeşitlendirebiliriz. Peki makalemizde ne anlatmaya çalışıyoruz? Bu makalede kaynak dosyası olarak düşündüğümüz şey HTML ve biz bir HTML dosyasını başka bir HTML dosyasında çağırmayı öğreniyor olacağız. Peki biz daha önce nasıl HTML’i başka bir HTML dosyasında çağırıyorduk, hemen onlara bakalım:

  • iframe : Şu an için gayet mantıklı bir çözüm gibi duruyor, fakat çok ağır. iframe’in içeriği sizin sayfanızdan tamamen farklı bir kaynağa bağlı ve sizin o sayfada bulunan HTML’e erişip şekillendirebilmeniz neredeyse imkansız.
  • AJAX : ‘xhr.responseType=“document” ‘leri çok severiz fakat bir HTML’i yükleyebilmek için javascript kullanmak mı? Kulağa pek hoş gelmiyor.
  • CrazyHacks™ : Stringlere gömülü halde duran gizlenmiş commentler. (Örneğin: <script type="text/html">). Çok kötü bir seçim…

Buradaki ironiyi görebiliyor musunuz? İnternetin en temel kaynağı olan HTML’i import edebilmek için çok büyük ve gereksiz bir uğraş gerekiyor. Neyseki Web Components imdadımıza burada yetişiyor.

Başlarken

Web Componentlerinin bir parçası olan HTML Imports‘u HTML’i başka bir HTML’in içine katmak için kullanabiliyoruz. Ayrıca HTML’i include ederken herhangi bir kısıtlamaya maruz kalmıyorsunuz. HTML’i eklediğimizde CSS, Javascript, resim veya .html uzantılı bir dosya ne içerebiliyorsa onları da eklemiş oluyoruz. Başka bir deyişle HTML import, ilişkili HTML/JS/CSS dosyalarını eklemek için mükemmel bir araç.

Negatif Yönleri

O kadar övdük, şimdi de gelelim en büyük eksisine. Tarayıcı desteği. Maalesef şu anda kısıtlı tarayıcılar tarafından destekleniyor. Destekleyen tarayıcılar şu anda sadece Chrome ve Opera. Internet Explorer ve Safari gibi tarayıcılar destek vermiyor. Firefox ise, 32. sürümünden itibaren about:config’den girilip “dom.webcomponents.enabled” enabled yapılarak desteklenir hale getirilebiliyor. Ayrıca 35. sürümünden itibaren tam olarak destek vermeye başlayacak. Daha fazla detay merak edenler Can I Use linkine bakabilirler.

Temelleri

Sayfaya HTML import etmek çok basit. Headlerin arasına link rel=“import” olarak tanımlamamız yeterli:

<head>
  <link rel="import" href="/dosya-yolu/dosya.html">
</head>

Başka bir domainden dosya transferi yapmak için import edilecek yerin CORS(cross-origin resource sharing)-enabled Olması gerekiyor.

<!-- Başka bir domainden alınan kaynakların CORS-enabled Olması gerekli -->
<link rel="import" href="http://example.com/elements.html">
Not:

Tarayıcının ağ yığını otomatik olarak aynı URL’den alınan istekleri teke indirir. Yani istek yapılan aynı URL’den sadece bir cevap alınır. Kaç kere aynı konumdan import edildiği önemli değil, sadece bir kere uygulanır.

Özellik Belirleme ve Destek

function supportsImports() {
  return 'import' in document.createElement('link');
}

if (supportsImports()) {
  // destekliyor!
} else {
  // HTML dosyalarını yüklemek için diğer kütüphaneleri kullanmamız gerekiyor.
}

Daha önce de negatif yönlerinde belirttiğim gibi her tarayıcı tarafından desteklenmiyor. Chrome 31 ile daha yeni yeni bu özelliği görmeye başlamıştık. Chrome 36 ile de tam yerine oturdu denilebilir. Opera da 23. versiyon ile işler tıkırında gibi. Fakat diğer tarayıcılar için aynı şeyi söylemek zor. Bundan dolayı HTML Import yaygınlaşana kadar diğer tarayıcıları da desteklemesi açısından Polymer Project‘in HTML Import‘unu kullanmak en mantıklısı gibi duruyor. Başka bir yazımda Polymer Project hakkında bahsedeceğim. Çok konu dışına çıkmamak için kısa kesip devam ediyoruz.

Kaynakları Gruplama

Import bize HTML/CSS/JS gibi kaynakları (hatta diğer HTML importları) bir import edilecek dosyada gruplayabilmek için güzel bir eğilim sunuyor. Bu noktada gerçekten etkili bir özellik. Bir tema, kütüphane ya da bir uygulama yazdığınızı düşünün. JS/CSS ve daha bir sürü import yapılması gerekiyor. Bunların hepsini tek bir dosyada depolayıp sonra da o dosyayı esas ekleyeceğiniz HTML dosyasına import edin. Ta da! Tek bir HTML import ile bir sürü importu halletmiş olduk. Artık bunları başka bir dosyaya taşımamız gerektiğinde sadece bir tane import kopyalamamız yeterli olacaktır.

Gerçek hayattan bir örnek vermek gerekirse Bootstrap tam da bu örneğimize uyuyor. Bootstrap bir sürü CSS, JS ve font gibi dosyaları kapsar, jQuery eklentilerine ihtiyaç duyar ve bir çok işaretleme örnekleri temin eder. Import Bootstrap gibi bir projede çok fazla esneklik sunabilir. İleride Bootstrap şu şekilde kullanılabilir bir hal alacaktır:

<head>
  <link rel="import" href="bootstrap.html">
</head>

Kullanıcı sadece bir html dosyası ekler. Onun için daha karışık bir kullanıma ihtiyaç yoktur. Bootstrap ile ilgili ihtiyacı olan her şey zaten o html dosyasının içindedir. bootstrap.html:

<link rel="stylesheet" href="bootstrap.css">
<link rel="stylesheet" href="fonts.css">
<script src="jquery.js"></script>
<script src="bootstrap.js"></script>
<script src="bootstrap-tooltip.js"></script>
<script src="bootstrap-dropdown.js"></script>
...

<!-- scaffolding markup -->
<template>
  ...
</template>

Heyecan verici bir özellik. Şimdilik bu burda kalsın, biz devam edelim.

Yüklenme/Hata Olayları

<link> elementi import başarılı bir şekilde tamamlandıysa load olayını başlatır, eğer deneme başarısızsa(Örneğin: 404) o zaman onerror olayını başlatır. Tarayıcı importları <link> gördüğü an yüklenmeye çalışır. İlerideki baş ağrılarının önüne geçmek için link elementine onload/onerror attributeleri eklemeliyiz.

<script async>
  function handleLoad(e) {
    console.log('import yüklendi: ' + e.target.href);
  }
  function handleError(e) {
    console.log('import yüklenirken hata: ' + e.target.href);
  }
</script>

<link rel="import" href="file.html"
      onload="handleLoad(event)" onerror="handleError(event)">
İpucu:

Yukarıda görüldüğü üzere olay yakalayıcı(event handlers) fonksiyonlarını importun üstüne yazdık. Tarayıcı import tagını gördüğü anda uygulamaya başlar. Eğer gördüğü anda script dosyamız daha okunmadıysa, tarayıcı konsolunda belirsiz fonksiyon adı hatası alırsınız ve fonksiyonlar işlenmez.

Aynı zamanda importu dinamik olarak oluşturabilirsiniz:

var link = document.createElement('link');
link.rel = 'import';
link.href = 'file.html'
link.onload = function(e) {...};
link.onerror = function(e) {...};
document.head.appendChild(link);

İçeriği Kullanmak

Bir sayfayı include etmek onu doğrudan sayfada yazdırmak anlamına gelmiyor. “Parser, git benim için bu dökümanı getir ki ben istediğim zaman bu dökümanı kullanabileyim.” anlamına geliyor. İçeriği kullanabilmemiz için script yazmamız gerekli. Burada önemli nokta, importun sadece bir döküman olduğu. Artık bu gelen dökümanı standart DOM APIlerini kullanarak manipüle edebiliriz. Biz herhangi bir şey yapmazsak ekranda aldığımız veri gözükmeyecektir.

Import’un içeriğine erişebilmek için elementin .import propertysini kullanmamız gerekiyor.

var content = document.querySelector('link[rel="import"]').import;

Aşağıdaki koşullardan birinin sağlanması durumunda link.import null değer döndürür:

  • Tarayıcı HTML Importu desteklemiyor ise.
  • <link> rel=“import” attribute’üne sahip değil ise.
  • <link> DOM’a eklenmemiş ise.
  • <link> DOM’dan silinmiş ise.
  • Kaynak dosyası CORS-enabled değil ise.
Tam Örnek

uyari.html bunları içeriyor diyelim:

<div class="uyari">
  <style scoped>
    h3 {
      color: red;
    }
  </style>
  <h3>Uyarı!</h3>
  <p>Bu sayfa yapım aşamasındadır!</p>
</div>

<div class="zaman-asimi">
  <h3>Dikkat et!</h3>
  <p>Bu içerik zaman aşımına uğramış olabilir!</p>
</div>

Import eden gelen html verisinin belirli bir bölümünü alabilir ve sayfaya kopyalayabilir:

<head>
  <link rel="import" href="warnings.html">
</head>
<body>
  ...
  <script>
    var link = document.querySelector('link[rel="import"]');
    var content = link.import;

    //uyari.html'in içerisinden bir DOM yakalıyoruz.
    var el = content.querySelector('.uyari');

    document.body.appendChild(el.cloneNode(true));
  </script>
</body>

Import edilen sayfa ile script

Importlar ana dökümanda değiller. Ana dökümanın uydusu gibi ona bağlılar. Fakat importlar yine de sanki ana dökümanımızdaymış gibi davranabilir. Bir import kendi dökümanındaki DOM’a erişebildiği gibi, onu import eden dökümanın DOM’una da erişebilir.

Örnek:
<link rel="stylesheet" href="http://www.example.com/styles.css">
<link rel="stylesheet" href="http://www.example.com/styles2.css">

<style>
  /* Not: Importta bulunan >style< ana dökümandaki elementleri de etkiler. Bu nedenle ana dökümana da ayrı olarak eklenmesine gerek yoktur. */
  #somecontainer {
    color: blue;
  }
</style>
...

<script>
  // importDoc importun dökümanını refere ediyor.
  var importDoc = document.currentScript.ownerDocument;

  // mainDoc importun yapıldığı ana dökümanı refere ediyor.(Bu sayfayı çağıranı)
  var mainDoc = document;

  // Bu importtaki ilk stil dosyasını alıyoruz ve
  // onu import eden dökümana ekliyoruz(klonluyoruz).
  var styles = importDoc.querySelector('link[rel="stylesheet"]');
  mainDoc.head.appendChild(styles.cloneNode(true));
</script>

Yukarıda küçük bir import dökümanı bulunuyor. Burada neler yaptığımızı tekrar anlatacak olursak; scriptin içinde import dökümanını(document.currentScript.ownerDocument) ve ana(import eden) dökümanı tanımlıyoruz. Ardından import dökümanındaki ilk stil dosyasını alıp bunu ana dökümanımıza ekliyoruz. Bana soracak olursanız oldukça harika.

Importta bulunan script hem doğrudan işlenebilir, hem de fonksiyon olarak tutulup ihtiyaç olduğunda ana dökümanda çağrılarak kullanılabilir.

İmporttaki Javascript kuralları:

  • Importun içindeki script içeriğin bulunduğu pencerede işlenir. Yani window.document ana dökümanı refere eder. Bunun bize iki avantajı bulunuyor:
    • Importta tanımlanan fonksiyonlar window(ana döküman)da geçerli olur.
    • Ana dökümanın içinde importun <script> taglarini append etmenize gerek kalmaz. Zaten importun içinde script gördüğünde hemen işlenir.
  • Importlar ana dökümanın parse edilmesini engellemez. Fakat içindeki scriptler sırayla işlenirler. Bunun anlamı, ana dökümanınızdaki scriptler bir nevi ertelenerek işlenirler.

Şimdilik bu kadar. Umarım faydalı bir yazı olmuştur. Yazımızın ikinci kısmında Web companentlerinin iletimi, performans faktörlerini göreceğiz ve son olarak unutmamamız gereken şeylere bakarak kısa bir tekrar yapacağız.

Yazının ikinci kısmına buradan ulaşabilirsiniz.