Responsive Imagemap

Ein Relikt aus dem Web 1.0 ist die Imagemap, bei der man Klickflächen über ein Bild positionieren kann. Der Vorteil zum normalen "Link um Bild" ist, dass dabei geometrische Formen und individuelle Polygone verwenden können, also z.B. eine Form die im Bild sichtbar ist, nachgebildet werden kann. Die Positionswerte dieser Klickflächen basieren allerdings auf Pixelwerten, und hier beginnt unser Problem.

Das Bild responsive zu machen, ist nicht schwer:

img {
  max-width: 100%;
  height: auto;
}

Die Imagemap-Area mit der Klickfläche passt sich allerdings nicht an, also auch wenn das Bild verkleinert auf ein Smartphone-Bildschirm passt, ist die Klickfläche noch immer in der Ausgangsgröße.

Abhilfe schafft hier ein jQuery-Plugin von Matt Stow namens RWD-ImageMap , das die Klickflächen automatisch je Screen-Größe umrechnet. Die Einbindung ist einfach:

//= require "vendor/jquery.min.js"
//= require "vendor/jquery.rwdImageMaps.min.js"
$('img[usemap]').rwdImageMaps();

Soweit so gut. Problem ist allerdings, dass man die Klickflächen nur mit einer Maus (oder Stylus) erfahren (im Sinne von mouseover) kann. Auf einem Touchdisplay hat man keinerlei visuelle Hilfen (Mauszeiger wechselt). Man müsste auf gut Glück überall tappen um zu erfahren, ob ein Bild eine Imagemap ist, in der bestimmte Bereiche verlinkt sind. Das ist natürlich problematisch und sollte bei der Konzeption bereits bedacht werden. Ich würde grundsätzlich davon abraten eine Imagemap im klassischen Sinne zu verwenden.

Eine Idee von Dudley Storey beschreibt eine Umsetzung mithilfe von SVG. Hier sollte man beachten, dass SVG direkt im HTML eingebunden unter Android 2.3 oder im Opera Mini nicht funktioniert. Weiterhin bin ich der Meinung, dass man die Klickflächen in SVG einen Rand geben sollte (zumindest für touchfähige Geräte), damit man sie eher als solche wahrnimmt.

Responsive Imagemap mit Akkordion-Liste

In einem Projekt war eine responsive Imagemap allerdings Anforderung. Glücklicherweise waren die Klickflächen direkt im Bild farblich hinterlegt, so dass das den Eindruck einer Schaltfläche ergab. Der Tap auf eine visuelle Schaltfläche sollte den Beschreibungstext unterhalb der Imagemap einblenden.

Ein paar Zeilen jQuery blendet die Texte ein und wieder aus:

$('area').on('click', function(e) {
  e.preventDefault();
  var target = $(this).attr('href');
  var imagemapItems = $('.imagemap__item');
  imagemapItems.hide();
  $(target).show();
  $(target +' > h3').addClass('is-open');
});

Das CSS dazu (Desktopansicht):

@media screen and (min-width: 48em) {
  .imagemap__item {
    display: none;
  }
  .imagemap__item:target {
    display: block;
  }
  .imagemap__item:target > .imagemap__text {
    display: block;
  }
}

In der Smartphone-Ansicht bis maximal 600px (37.5em) sollte das Imagemap-Bild und die Überschriften der beschreibenden Texte angezeigt werden und als Akkordion dargestellt werden. Die Imagemap-Funktionalität sollte verhindert werden. Einerseits weil die Schaltflächen zu klein werden, andererseits weil der User nicht unbedingt eine Imagemap auf einem Touch-Device erwartet.

Außerdem ist es problematisch, dass man die ImageMap-Klickflächen nicht mit CSS stylen kann. Man kann sie weder Off-Canvas positionieren, noch mit display: none; ausblenden. Einzige Möglichkeit, das ImageMap-Markup mit JavaScript löschen.

// remove ImageMaps Markup
var imageMaps = document.querySelectorAll(".m-imagemap map");
[].forEach.call( imageMaps, function(el) {
    el.parentNode.removeChild(el);
});

Wie man in dem Beispiel sehen kann, werden hier Standard HTML5 Javascript Funktionen und nicht jQuery verwendet. In dem Projekt wurde sehr auf Performance geachtet. So wurde erst ab dem Bereich "Tablet" jQuery hinzu geladen. Smartphones mit HTML5-Unterstützung können anhand von CSS-Selektoren sowohl eine Auswahl generieren document.querySelectorAll, als auch Klassen im DOM toggle’n classList.toggle().

Eine einfache HTML5 class-toggle-Funktion mit Vanilla-JS:

// select all H3 headers in .imagemap__item
var imageMapList =  document.querySelectorAll('.imagemap__item h3');

// on click handler to toggle .imagemap__text visibility
[].forEach.call(imageMapList, function(el) {
   el.addEventListener('click', function() {
     this.classList.toggle('is-open');
  }, false);
});

CSS:

.js .imagemap__text {
  display: none;
}
.imagemap__item .is-open + .imagemap__text {
  display: block;
}

Für Android 2.3 muss man allerdings einen classList-Polyfill einbinden um die toggle Funktion zu nutzen (classList.toggle wird erst ab Android 4 unterstützt). Mit Modernizr testet man ob der Viewport maximal 600px breit ist und ob classList nicht unterstützt wird. Wenn der Test true ergibt, wird die Polyfill-Datei (classList.min.js) geladen.

Modernizr.load([{
  test: Modernizr.mq("only screen and (max-width: 600px)") && !Modernizr.classlist,
  yep: "./js/classList.min.js";
}]);

Ich hab mal versucht ein reduziertes Beispiel aus dem Projekt heraus zu eisen und separat zusammen zu bauen: Demo Responsive Image Map

Wie gesagt, technisch alles machbar, aber manchmal ist es besser die Inhalte konzeptionell zu ändern. Das bedingt aber einen anderen Prozess. Aber das ist ein anderes Thema…