Popups die zich gedragen

Precies een jaar geleden verscheen het artikel Links die zich gedragen door Peter Nederlof op Naar Voren. Hierin werd onder andere uitgelegd hoe je op een “unobtrusive” manier popup-links kunt realiseren. Het enige probleem hierbij is dat je van tevoren moet aangeven hoe groot je popupvenster moet worden. En uiteraard kan dit nogal eens verschillen, met name als het afbeeldingen betreft. Hoe los je dit probleem op? AJAX!

We gaan er in dit artikel vanuit dat de popups gebruikt worden voor het bekijken van grote afbeeldingen, zoals dat gedaan wordt bij File Under of Majestic Moose. Na het lezen van Peterned’s artikel kunnen we de volgende verbetering aanbrengen. Voor:

<a href="/images/voorbeeld.jpg" onclick="window.open(this.href,'popup','width=500,height=400');return false;">Bekijk het voorbeeld</a>

Na:

<a href="/images/voorbeeld.jpg" rel="popup">Bekijk het voorbeeld</a>

AJAXificeren

De code om de rel="popup" af te handelen staat in een apart JavaScript bestand, waarin het commando aangeroepen wordt dat eerst in het onclick-event stond, met als nadeel: elk venster zal 500 bij 400 pixels groot worden. Daar gaan we wat aan doen. Ik gebruik de Prototype library voor het afhandelen van de Ajax requests. Als eerste definiëren we een variabele waar het resultaat in komt te staan.

var res;

Dan roepen we de volgende functie in het leven, die wordt aangeroepen na het laden van de pagina:

function initPopups() {
  if (!document.getElementsByTagName) return false;

  var A    = document.getElementsByTagName('a');
  var pops = new Array();

  for (var i = 0; i < A.length; i++) {
    if ('popup' == A[ i ].rel) {
      pops[pops.length] = A[ i ].href;
      A[ i ].onclick = function() { return popIt(this); }
    }
  }

  if (pops.length) {
    var url  = '/popup.php';
    var pars = 'imgs='+pops.join(',');
    var req  = new Ajax.Request(
      url,
      {
        method: 'get',
        parameters: pars,
        onComplete: handleResponse
      }
    );
  }
}

Dan moeten we de functie handleResponse nog aanmaken:

function handleResponse(req) {
  res = eval("(" + req.responseText + ")");
}

En de functie popIt is ook onmisbaar:

function popIt(anch) {
  if (!res.popups) return true;
  for (var j = 0; j < res.popups.length; j++) {
    if (res.popups[j].href == anch.href) {
      window.open('/popup.php?show='+anch.href, '_blank', res.popups[j].wh);
      return false;
    }
  }
  return true;
}

Tenslotte moeten we doe boel activeren op het moment dat de pagina geladen is. Met behulp van Prototype gaat dat zo:

Event.observe(window, 'load', initPopups, false);

De kneep

De functie initPopups() loopt alle links op de pagina langs en verzamelt degene met rel="popup". Aan elk van die links wordt de functie popIt() gekoppeld aan het onclick-event. Vervolgens worden die links naar het php-script popup.php (bron) gestuurd. Dit script achterhaalt de breedte en hoogte van de afbeelding door de functie getimagesize() en genereert JavaScript code (in JSON) waarmee het object res gevuld kan worden. Hierin zitten de hrefs met bijbehorende dimensies van de afbeelding. popIt() loopt dat object door en opent een venster met de juiste grootte.

Wanneer de popup-link aangeklikt wordt, opent een popup-venster met als bron: alweer popup.php, maar dit keer genereert het script een simpel HTML bestand zonder marges, met het opgevraagde plaatje erin.

Voor het gemak zetten we popup.php in de root van de website. Je kunt het ook ergens anders plaatsen, zolang je het complete script er maar op aanpast.

Voorbeelden zien? Wat plaatjes uit den oude doosch: zonnetje, mengtafel en mist.

Update: De eerste beta versie van deze verzameling scripts was gevoelig voor popup-blockers. De versie die u nu ziet werkt wel naar behoren.

Update 2: De echte doe-het-zelvert gebruikt natuurlijk Lightbox in plaats van lelijke popups.

Reacties

  1. author
    1 Ronald 7 december 2005, 18:00

    Firefox blokkeert ze behoorlijk. En als je de popupwindow open laat staan en op een andere afbeelding klikt wordt het plaatje in het openstaande popup venster geladen, ook al past dat niet. (getest met winME en FF 1.5)

  2. author
    2 Low 7 december 2005, 18:31

    @Ronald: ik ben nog op zoek naar een omweg voor dat eerste. Dat tweede is al opgelost.

  3. author
    3 Ronald 8 december 2005, 10:37

    Toch moet het ook mogelijk zijn het popupvenster te resizen bij een ander formaat foto (zit dat niet in pivot?), nu zit je met een 3tal open popup vensters. (niet dat dat ernstig is, het zijn zomaar wat gedachten die ik er bij heb)

  4. author
    4 George 8 december 2005, 11:34

    Koel. Ik hou je in de gaten :D

  5. author
    5 Low 8 december 2005, 11:46

    Pivot genereert in ieder geval een hoop “obtrusive” code bij elke popup link. Dat werkt wel, maar is niet heel netjes. Bovendien worden daar de vensters niet ge-resized, maar sluit het venster op het moment dat de focus verdwijnt. Ik wil graag zelf bepalen wanneer ik een venster sluit. Dan kan ik zo nu en dan meerdere plaatjes tegelijk kijken. :-)

  6. author
    6 Milo 8 december 2005, 13:08

    Hear hear!

  7. author
    7 RaDDIX 10 december 2005, 12:27

    De pop-up wordt behoorlijk weergegeven (en niet geblokkeerd) in win/ff 1.5.
    Net script imo, en het is eens wat anders dan een zoveelste script dat alle href classes afloopt.

  8. author
    8 Peterned 12 december 2005, 17:02

    * 2 thumbs up *
    Voor het oog ook een stuk rustiger dan client-side de img afmetingen checken en daarna resizen, wat bovendien ook nog geblocked kan zijn. top :)

  9. author
    9 Pelle 14 december 2005, 16:00

    Aardige oplossing opzich; jammer dat er een stukje serverside scripting bij komt kijken.
    Ook eval() vind ik vies, daar is echt wel een andere oplossing voor te verzinnen :)

  10. author
    10 Low 14 december 2005, 16:40

    @Pelle: als je een manier weet om client-side de dimensies van een afbeelding te achterhalen voordat het popup venster geopend wordt, hoor ik dat graag.

    En wat eval betreft: in combinatie met XMLHttpRequest is dat zo vies nog niet.

  11. author
    11 Pelle 14 december 2005, 16:49

    Het is niet mogelijk om dat voor het openen van de popup te doen inderdaad (tenzij je vantevoren alle images preload in een apart iframe ofzo, maar dat is ook niks).

    Maar ik vraag me af waarom die dimensies vantevoren bekend moeten zijn; ik heb er totaal geen moeite mee dat een popup-met-foto pas na het laden van de foto de goeie grootte krijgt.

    Over het eval()-verhaal: ja, het is makkelijker. Maar ik blijf erbij dat het een smerige functie is. Waarom niet een XML-document teruggeven en dat parsen met de normale DOM functies? Het is niet voor niets een XMLHttpRequest ;)
    Ja, iets meer moeite, maar wel veel netter.

  12. author
    12 Low 14 december 2005, 17:52

    Tja, dat is dan een kwestie van smaak. Persoonlijk kan ik er niet tegen als een venster ongevraagd van grootte verandert, vandaar dat ik het van tevoren wil aangeven.

    Ik had trouwens hier nog een andere variant van, zonder AJAX of serverside toestanden: bij een klik op een popup link een div aanmaken met position: fixed, 100% hoog en breed, met de afbeelding als background-image er middenin (en onclick = function() { self.style.display = "none"; } o.i.d.). Werkt leuk in niet-IE.

    En als ik weer wat meer tijd heb, zal ik jouw xml-document-suggestie eens nader onderzoeken. :)

  13. author
    13 Pelle 14 december 2005, 17:58

    Websites die mijn browser opeens resizen krijgen van mij ook meteen een ALT-F4, maar voor het tonen van een foto in een popup heb ik er totaal geen moeite mee :)

    Foto’s in een div tonen is ook een prima oplossing inderdaad, daarmee omzeil je in ieder geval alle te strak ingestelde popupkillers.

  14. author
    14 Jeroen 22 december 2005, 11:17

    Wat ik niet zo goed begrijp is waarom hier wordt gekozen voor een niet-compatibele werkwijze, terwijl er een simpel JavaScript-module kan worden geschreven.

    Een voorbeeld hiervan vind je bijvoorbeeld op http://www.cupido-antiek.nl

    Alle popups zijn dezelfde pagina, en er wordt gewoon berekend hoe groot de afbeelding is, en vervolgens wordt de boel op maat gemaakt met eventuele extra ruimte voor het logo etc.

    Het is wel leuk, al die nieuwe technieken, en ik ben groot voorstander van AJAX, maar uiteindelijk draait het toch vooral om compatibility.

  15. author
    15 Low 22 december 2005, 13:02

    Jeroen, wat is er niet-compatibel aan deze methode? Het php script? Dat zou natuurlijk ook een asp, ruby of ander server-side script kunnen zijn.

    Het voorbeeld dat je aangeeft is zelfs minder compatibel dan deze methode: zodra JavaScript uit staat, krijgt de gebruiker geen vergroting te zien. Bij deze AJAX-methode kom je toch nog bij het uiteindelijke plaatje, ookal staat JavaScript uit: graceful degradation.

    Bovendien wilde ik juist af van alle JavaScript in de broncode en verspringende vensters, wat allebei wel het geval is bij cupido antiek.

  16. author
    16 Jerome 12 januari 2006, 01:47

    Het spijt me zeer maar kom er dus niet aan uit, hij laad de popup niet.
    Klinkt miss als een noobie.. maar zou echt niet weten wat ik fout heb gedaan.

    Hij maakt geen verbinding met popup.php denk ik, hij weergeeft de image gewoon vergroot in hetzelfde venster.

  17. author
    17 Low 12 januari 2006, 09:23

    @Jerome: gebruik je misschien Safari? Ik geloof dat die browser nog wat moeite ermee heeft. Zo niet, heb je een url waar je dit geprobeerd hebt?

  18. author
    18 Jerome 12 januari 2006, 16:56

    @Low is er een mail adres waar ik het naartoe kan mailen?
    site is nog niet voor het publiek zichtbaar, wil dit graag zo houden..

    Eventueel msn?

    Groeten,
    Jerome

  19. author
    19 Low 12 januari 2006, 17:04

    @Jerome: low at loweblog punt com.

Reageren is niet mogelijk op dit bericht.

Zoeken in loweblog.com