Nehmen wir an, Sie haben eine Weltkarte und möchten das Land (oder den Bundesstaat, den Bezirk usw.) hervorheben können, wenn der Cursor darüber schwebt oder es angetippt wird.
Machbar!
Zuerst sollte die Karte wahrscheinlich vektoriel sein. SVG ist hier mit ziemlicher Sicherheit die richtige Wahl für das Bildformat. Es bietet uns eine gestochen scharfe Karte, wahrscheinlich in einer vernünftigen Größe, und vor allem die für uns notwendige Interaktivität und einfache Styling-Möglichkeit.
Nehmen wir als Beispiel eine Karte der US-Bundesstaaten. Wikipedia hat eine perfekte für uns.

Laden wir dieses SVG herunter und werfen einen Blick auf den Code

Hier gibt es ziemlich gut formatiertes SVG, wobei jeder Bundesstaat durch ein <path> mit einer eindeutigen ID dargestellt wird.
Die einfachsten möglichen Karten-Hovereffekte beinhalten das bloße Einfügen dieses SVG in den HTML-Code und das Hinzufügen eines :hover zu CSS wie
path:hover {
fill: red;
}

Aber bringen wir das auf die nächste Stufe.
Zwei-Wege-Hovereffekte
Damit meine ich: Sie können über den Bundesstaat fahren oder Sie können über den Namen des Bundesstaates in einer Liste fahren und Sie sehen ihn hervorgehoben.
Sagen wir, wir haben eine Liste von Bundesstaaten wie diese
<ul class="list-of-states">
<li>Alabama</li>
<li>Alaska</li>
<li>Arizona</li>
<li>Arkansas</li>
...
In dem SVG, das wir von Wikipedia erhalten haben, waren die Pfade so:
<path id="AK" fill="#D3D3D3" d="..." />
Leider gibt es derzeit keine einfache, existierende Methode, diese Elemente programmatisch zu verbinden. Ich habe diese Verbindung von Hand erstellt, indem ich für jeden Bundesstaat in der Liste ein data-*-Attribut verwendet habe
<ul class="list-of-states">
<li data-state="AL">Alabama</li>
<li data-state="AK">Alaska</li>
<li data-state="AZ">Arizona</li>
<li data-state="AR">Arkansas</li>
...
Jetzt haben wir, was wir brauchen.
Wir werden einen Event-Handler sowohl für das Einfahren mit der Maus in die Listenelemente als auch für das Herausfahren damit binden. Wenn diese Dinge passieren, ermitteln wir über das Datenattribut, welcher SVG-Bundesstaat relevant ist, und fügen dann Klassen zu beiden hinzu.
var wordStates = document.querySelectorAll(".list-of-states li");
wordStates.forEach(function(el) {
el.addEventListener("mouseenter", function() {
var stateCode = el.getAttribute("data-state");
var svgState = document.querySelector("#" + stateCode);
el.classList.add("on");
svgState.classList.add("on");
});
el.addEventListener("mouseleave", function() {
var stateCode = el.getAttribute("data-state");
var svgState = document.querySelector("#" + stateCode);
el.classList.remove("on");
svgState.classList.remove("on");
});
});
Jetzt können wir diesen Klassennamen verwenden, um alles nach unseren Wünschen zu stylen.
.list-of-states li.on {
background: red;
color: white;
font-weight: bold;
}
path.on {
fill: red;
}

Wir können genau das Gegenteil für die Pfade der Bundesstaaten tun, so dass der Bundesstaat in der Liste hervorgehoben wird, wenn wir mit der Maus über die Karte fahren
var svgStates = document.querySelectorAll("#states > *");
svgStates.forEach(function(el) {
el.addEventListener("mouseenter", function() {
var stateId = el.getAttribute("id");
var wordState = document.querySelector("[data-state='" + stateId + "']");
el.classList.add("on");
wordState.classList.add("on");
});
el.addEventListener("mouseleave", function() {
var stateId = el.getAttribute("id");
var wordState = document.querySelector("[data-state='" + stateId + "']");
el.classList.remove("on");
wordState.classList.remove("on");
});
});
DRYing up (Vermeidung von Wiederholungen)
Leider ist das viel repetitiver Code. Also räumen wir ihn auf. Wir können die Funktionalität abstrahieren in
- Hinzufügen des "An"-Zustands (durch Hovern über die Karte)
- Hinzufügen des "An"-Zustands (durch Hovern über die Liste)
- Entfernen aller "An"-Zustände
Wir können diese in Funktionen umwandeln, die wir bei Bedarf aufrufen können
var wordStates = document.querySelectorAll(".list-of-states li");
var svgStates = document.querySelectorAll("#states > *");
function removeAllOn() {
wordStates.forEach(function(el) {
el.classList.remove("on");
});
svgStates.forEach(function(el) {
el.classList.remove("on");
});
}
function addOnFromState(el) {
var stateCode = el.getAttribute("data-state");
var svgState = document.querySelector("#" + stateCode);
el.classList.add("on");
svgState.classList.add("on");
}
function addOnFromList(el) {
var stateId = el.getAttribute("id");
var wordState = document.querySelector("[data-state='" + stateId + "']");
el.classList.add("on");
wordState.classList.add("on");
}
wordStates.forEach(function(el) {
el.addEventListener("mouseenter", function() {
addOnFromState(el);
});
el.addEventListener("mouseleave", function() {
removeAllOn();
});
});
svgStates.forEach(function(el) {
el.addEventListener("mouseenter", function() {
addOnFromList(el);
});
el.addEventListener("mouseleave", function() {
removeAllOn();
});
});

Mobile Unterstützung
Haben Sie schon gehört? Auf (den meisten) Touchscreens gibt es keinen einzelnen Cursor.
Wir können es aber ganz einfach mit Taps zum Laufen bringen, was ein Grund für das DRYing war. Zusätzlich zur Bindung an mouseenter und mouseleave, binden wir auch touchstart (click würde auch funktionieren).
wordStates.forEach(function(el) {
// other vents
el.addEventListener("touchstart", function() {
removeAllOn();
addOnFromList(el);
});
});
svgStates.forEach(function(el) {
// other events
el.addEventListener("touchstart", function() {
removeAllOn();
addOnFromState(el);
});
});
Demo
Siehe den Pen
Hoving States von Chris Coyier (@chriscoyier)
auf CodePen.