Weboldal "AJAX"-osítása egyszerűen 2

Tovább gondolkodtam a Weboldal “AJAX”-osítása egyszerűen bejegyzéssel kapcsolatban, és rájöttem, hogy maradt pár hiányosság, amit nem kezeltem le. Az egyik ilyen az, hogy mi van akkor ha a linkek nem a gyökértől indulnak, a másik pedig az, hogy mi van, ha <base> elemet használnak az oldalon. Most ezeket is megoldom, és szerintem egy eléggé szép, ugyanakkor majd minden problémára kiterjedő megoldást találtam.

Az egyik probléma, ha <a href="blog.html">Blog</a> jellegű linkeket használnak az oldalon, azaz az linkelt oldal URL-je a mostanihoz képest relatív. Ezt úgy küszöbölhetjük ki, hogy ha ilyen linkekkel találkozunk, akkor azokat átalakítjuk a megfelelő formára. És ha már itt vagyunk, akkor az abszolút URL-lel megadott linkeket is átalakítjuk, így később már csak egyetlen esetet kell kezelni. Ezzel nem csorbul a használhatóság, csupán közös formára hoztuk az összeset.

Ezzel kapcsolatban eszembe jutott, hogy mi van akkor, ha az oldalon <base> elemmel befolyásolják ezen URL-eket. A <base href="http://example.com/blog/"> elem annyit tesz, hogy a <a href="blog.html">Blog</a> formában megadott linkek a <base> által megszabott URLhez képest relatívak, míg <base> nélkül pedig az aktuális oldalhoz képest lennének relatívak. Ami még előjött probléma ezzel kapcsolatban, hogy mit tekintünk aktuális oldalnak.

Amennyiben még nem volt AJAX eseményünk, akkor az aktuális oldal a tényleges aktuális oldal, míg ha volt, akkor a tárolónkon belüli a tároló maga lesz az. Mint látszik ez eléggé megbonyolította a kód működését, ezért majdnem az egészet teljesen újra kellett építeni. Készítettem is két segédfüggvényt. Az egyik megnézi, hogy van-e <base> elem a megadott elemben, és ha igen, akkor annak visszaadja a belső részét. Ebből csinálhattam volna egyszerűen egy jQuery kiegészítőt, de nem éreztem szükségét. A másik pedig az átadott elemben átalakítja a linkeket, hogy azok a gyökérhez képest relatívak legyenek.

Igazából csak ezek változtak, többi működés nagyjából maradt a régi, de kód szintjén így is nagyok a változások.

jQuery(function ($) {
  var reInternal = $.urlInternalRegExp(),
      id = '#main',
      tag = 'a',
      title = document.title,
      l = window.location,
      baseRe = /[^\/]+$/,
      body = $(document.body),
      // Az alap URL lekérdezése, amennyiben van <base> elem.
      getBase = function (elem) {
        var docBase = elem.find('head > base[href]'),
            match = docBase.length && docBase.attr('href').match(reInternal);

        if (match) {
          return match[1];
        }
      },
      base = getBase(body) || l.pathname.replace(baseRe, ''), // Relatív URN
      containerBase = base,             // A tároló relatív URNje
      container = $(id),                // A tároló
      defaultValue = container.html(),  // Eltároljuk az eredetit
      // Az elem összes linkjét relativvá tesszük a gyökértől.
      makeRelative = function (elem) {
        elem.find(tag + ':urlInternal(href)').attr('href', function (index, href) {
          var match, urn, link = this;
          // jQuery 1.3 támogatás
          if (typeof href === 'undefined') {
            href = $(this).attr('href');
          }

          // Megnézzük hogy abszolút URI-nek van-e álcázva,
          // ha igen, akkor leszedjük róla az abszolút részt.
          match = href.match(reInternal);
          if (match) {
            urn = href.substring(match[0].length - 1);
          }
          else if (href.charAt(0) === '/') {
            // A domainhez képest abszolút
            urn = href;
          }
          else {
            // Az aktuális oldalhoz képest relativ
            urn = (container.find(tag).filter(function () {
                return this === link;
              }).length ? containerBase : base) + href;
          }

          return urn;
        });
      };

  // Bármilyen linkre kattintunk, lekezeljük
  $(tag).live('click', function (event) {
    var link = $(this),
        href = link.attr('href');

    // Ha a link belső URL, akkor ahelyett hogy odaugranánk,
    // csak megváltoztatjuk a hash-t.
    if ($.isUrlInternal(href)) {
      // Megváltoztatjuk a hash-t az URI-re
      l.hash = '!' + href.replace(/#.*/, '');
      // Megakadályozzuk az eseményt.
      event.preventDefault();
    }
  });

  // Kezeljük a megváltozott hash eseményt, és kiváltjuk is, hátha már van hash.
  $(window).hashchange(function (e) {
    var hash = l.hash,
        match = hash && hash.match(/^#?!(\/.*)/),
        urn = match && match[1];

   // Ha megfelelő formátumú a hash, akkor betöltjük a tartalmat a tárolóba.
   if (urn) {
      // A gyermekekre van csak szükségünk.
      $.get(urn, function (data) {
        var rscript = /<script(.|\s)*?\/script>/gi, // Script elemek nem maradnak
            page = jQuery("<div />").append(data.replace(rscript, "")),

        // Betöltődött a tartalom, megnézzük van-e cím, és frissítjük az oldal címét
        containerBase = getBase(page) || urn.replace(baseRe, '');
        document.title = page.find('head > title').text() ||
              page.find('h1:first').text() || title;

        makeRelative(container.html(page.find(id).html()));
      });
    }
    else if (!hash || hash === '#') {
      // Visszaállítjuk az eredeti tartalmat
      container.html(defaultValue);
    }
  }).hashchange();

  // Módosítjuk a linkeket, hogy relatívok legyenek
  makeRelative(body);
});

A működés megtekinthető a tesztoldalon.

comments powered by Disqus