Ich mochte Tag-Clouds schon immer. Ich mag die UX, indem ich durch die relative Schriftgröße der Tags sehe, welche Tags auf einer Website am beliebtesten sind, wobei beliebte Tags größer sind. Sie scheinen aus der Mode gekommen zu sein, obwohl man oft Versionen davon in Illustrationen in Tools wie Wordle sieht.
Wie schwierig ist es, eine Tag-Cloud zu erstellen? Gar nicht sehr schwierig. Sehen wir mal!
Beginnen wir mit dem Markup
Für unser HTML werden wir jeden unserer Tags in eine Liste packen, <ul class="tags"><ul>. Wir werden dies mit JavaScript einfügen.
Wenn Ihre Tag-Cloud bereits in HTML vorliegt und Sie nur die relative font-size Sache machen möchten, ist das gut! Progressive Enhancement! Sie sollten das JavaScript später anpassen können, so dass es nur diesen Teil erledigt, aber nicht unbedingt die Tags selbst erstellt und einfügt.
Ich habe etwas JSON mock-up erstellt mit einer bestimmten Anzahl von Artikeln, die mit jeder Eigenschaft getaggt sind. Schreiben wir ein JavaScript, um dieses JSON-Feed abzurufen und drei Dinge zu tun.
Erstens erstellen wir ein <li> aus jedem Eintrag für unsere Liste. Stellen Sie sich vor, das HTML ist bisher so:
<ul class="tags">
<li>align-content</li>
<li>align-items</li>
<li>align-self</li>
<li>animation</li>
<li>...</li>
<li>z-index</li>
</ul>
Zweitens werden wir die Anzahl der Artikel, die jede Eigenschaft hat, in Klammern neben jedem Listenelement angeben. Also ist das Markup jetzt so:
<ul class="tags">
<li>align-content (2)</li>
<li>align-items (2)</li>
<li>align-self (2)</li>
<li>animation (9)</li>
<li>...</li>
<li>z-index (4)</li>
</ul>
Drittens und zuletzt erstellen wir einen Link um jeden Tag, der zur richtigen Stelle führt. Hier können wir die font-size Eigenschaft für jedes Element festlegen, je nachdem, mit wie vielen Artikeln diese Eigenschaft getaggt ist, sodass "animation" mit 13 Artikeln viel größer ist als "background-color", das nur einen Artikel hat.
<li class="tag">
<a
class="tag__link"
href="https://example.com/tags/animation"
style="font-size: 5em">
animation (9)
</a>
</li>
Der JavaScript-Teil
Werfen wir einen Blick auf das JavaScript, um dies zu tun.
const dataURL =
"https://gist.githubusercontent.com/markconroy/536228ed416a551de8852b74615e55dd/raw/9b96c9049b10e7e18ee922b4caf9167acb4efdd6/tags.json";
const tags = document.querySelector(".tags");
const fragment = document.createDocumentFragment();
const maxFontSizeForTag = 6;
fetch(dataURL)
.then(function (res) {
return res.json();
})
.then(function (data) {
// 1. Create a new array from data
let orderedData = data.map((x) => x);
// 2. Order it by number of articles each tag has
orderedData.sort(function(a, b) {
return a.tagged_articles.length - b.tagged_articles.length;
});
orderedData = orderedData.reverse();
// 3. Get a value for the tag with the most articles
const highestValue = orderedData[0].tagged_articles.length;
// 4. Create a list item for each result from data.
data.forEach((result) => handleResult(result, highestValue));
// 5. Append the full list of tags to the tags element
tags.appendChild(tag);
});
Das obige JavaScript verwendet die Fetch API, um die URL abzurufen, auf der tags.json gehostet wird. Sobald es diese Daten erhält, gibt es sie als JSON zurück. Hier sequenzieren wir in ein neues Array namens orderedData (damit wir das ursprüngliche Array nicht verändern), finden den Tag mit den meisten Artikeln. Wir werden diesen Wert später in einer Font-Skala verwenden, damit alle anderen Tags eine relative Schriftgröße dazu haben. Dann rufen wir für jedes Ergebnis in der Antwort eine Funktion auf, die ich handleResult() genannt habe, und übergeben result und highestValue als Parameter an diese Funktion. Sie erstellt auch
- eine Variable namens
tags, die wir verwenden werden, um jedes Listenelement einzufügen, das wir aus den Ergebnissen erstellen, - eine Variable für ein
fragment, um das Ergebnis jeder Iteration der Schleife zu speichern, das wir später an dietagsanhängen werden, und - eine Variable für die maximale Schriftgröße, die wir später in unserer Font-Skala verwenden werden.
Als Nächstes die Funktion handleResult(result)
function handleResult(result, highestValue) {
const tag = document.createElement("li");
tag.classList.add("tag");
tag.innerHTML = `<a class="tag__link" href="${result.href}" style="font-size: ${result.tagged_articles.length * 1.25}em">${result.title} (${result.tagged_articles.length})</a>`;
// Append each tag to the fragment
fragment.appendChild(tag);
}
Dies ist eine ziemlich einfache Funktion, die ein Listenelement erstellt, das der Variable namens tag zugewiesen wird, und dann dieser Listenelement eine Klasse .tag hinzufügt. Sobald dies erstellt ist, setzt sie die innerHTML des Listenelements auf einen Link und füllt die Werte dieses Links mit Werten aus dem JSON-Feed, wie z. B. einem result.href für den Link zum Tag. Wenn jedes li erstellt wird, wird es als String zum fragment hinzugefügt, das wir später an die Variable tags anhängen werden. Das wichtigste Element hier ist das Inline-style-Tag, das die Anzahl der Artikel – result.tagged_articles.length – verwendet, um eine relative Schriftgröße mit em-Einheiten für dieses Listenelement festzulegen. Später werden wir diesen Wert in eine Formel ändern, um eine grundlegende Font-Skala zu verwenden.
Ich finde dieses JavaScript etwas unschön und schwer zu lesen, also erstellen wir ein paar Variablen und eine einfache Font-Skala-Formel für jede unserer Eigenschaften, um es aufzuräumen und leichter lesbar zu machen.
function handleResult(result, highestValue) {
// Set our variables
const name = result.title;
const link = result.href;
const numberOfArticles = result.tagged_articles.length;
let fontSize = numberOfArticles / highestValue * maxFontSizeForTag;
fontSize = +fontSize.toFixed(2);
const fontSizeProperty = `${fontSize}em`;
// Create a list element for each tag and inline the font size
const tag = document.createElement("li");
tag.classList.add("tag");
tag.innerHTML = `<a class="tag__link" href="${link}" style="font-size: ${fontSizeProperty}">${name} (${numberOfArticles})</a>`;
// Append each tag to the fragment
fragment.appendChild(tag);
}
Durch das Festlegen einiger Variablen, bevor wir mit der Erstellung unseres HTML beginnen, ist der Code viel einfacher zu lesen. Und es macht unseren Code auch etwas DRYer, da wir die Variable numberOfArticles an mehr als einer Stelle verwenden können.
Sobald jeder der Tags in dieser .forEach-Schleife zurückgegeben wurde, werden sie im fragment gesammelt. Danach verwenden wir appendChild(), um sie dem tags-Element hinzuzufügen. Das bedeutet, dass das DOM nur einmal manipuliert wird, anstatt jedes Mal, wenn die Schleife läuft, was ein schöner Leistungsschub ist, wenn wir eine große Anzahl von Tags haben.
Font-Skalierung
Was wir jetzt haben, funktioniert für uns gut, und wir könnten mit dem Schreiben unseres CSS beginnen. Allerdings bedeutet unsere Formel für die Variable fontSize, dass der Tag mit den meisten Artikeln (was "flex" mit 25 ist) 6em sein wird (25 / 25 * 6 = 6), aber die Tags mit nur einem Artikel werden 1/25 so groß sein (1 / 25 * 6 = 0,24), was den Inhalt unlesbar macht. Wenn wir einen Tag mit 100 Artikeln hätten, würden die kleineren Tags noch schlechter abschneiden (1 / 100 * 6 = 0,06).
Um dies zu umgehen, habe ich eine einfache if-Anweisung hinzugefügt, die besagt: Wenn die zurückgegebene fontSize kleiner als 1 ist, setzen Sie die fontSize auf 1. Wenn nicht, behalten Sie sie bei ihrer aktuellen Größe. Jetzt werden alle Tags innerhalb einer Font-Skala von 1em bis 6em liegen, auf zwei Dezimalstellen gerundet. Um die Größe des größten Tags zu erhöhen, ändern Sie einfach den Wert von maxFontSizeForTag. Sie können entscheiden, was für Sie am besten funktioniert, basierend auf der Menge des Inhalts, mit dem Sie es zu tun haben.
function handleResult(result, highestValue) {
// Set our variables
const numberOfArticles = result.tagged_articles.length;
const name = result.title;
const link = result.href;
let fontSize = numberOfArticles / highestValue * maxFontSizeForTag;
fontSize = +fontSize.toFixed(2);
// Make sure our font size will be at least 1em
if (fontSize <= 1) {
fontSize = 1;
} else {
fontSize = fontSize;
}
const fontSizeProperty = `${fontSize}em`;
// Then, create a list element for each tag and inline the font size.
tag = document.createElement("li");
tag.classList.add("tag");
tag.innerHTML = `<a class="tag__link" href="${link}" style="font-size: ${fontSizeProperty}">${name} (${numberOfArticles})</a>`;
// Append each tag to the fragment
fragment.appendChild(tag);
}
Jetzt das CSS!
Wir verwenden Flexbox für unser Layout, da jeder der Tags unterschiedliche Breiten haben kann. Wir zentrieren sie dann mit justify-content: center und entfernen die Listenaufzählungspunkte.
.tags {
display: flex;
flex-wrap: wrap;
justify-content: center;
max-width: 960px;
margin: auto;
padding: 2rem 0 1rem;
list-style: none;
border: 2px solid white;
border-radius: 5px;
}
Wir werden Flexbox auch für die einzelnen Tags verwenden. Dies ermöglicht es uns, sie mit align-items: center vertikal auszurichten, da sie unterschiedliche Höhen haben werden, basierend auf ihren Schriftgrößen.
.tag {
display: flex;
align-items: center;
margin: 0.25rem 1rem;
}
Jeder Link in der Tag-Cloud hat ein kleines Polster, nur um ihn etwas außerhalb seiner strikten Abmessungen klickbar zu machen.
.tag__link {
padding: 5px 5px 0;
transition: 0.3s;
text-decoration: none;
}
Ich finde das besonders auf kleinen Bildschirmen praktisch für Leute, die es vielleicht schwieriger finden, auf Links zu tippen. Die anfängliche text-decoration wird entfernt, da ich denke, wir können davon ausgehen, dass jedes Textelement in der Tag-Cloud ein Link ist und daher keine besondere Dekoration dafür benötigt wird.
Ich füge ein paar Farben hinzu, um die Dinge etwas aufzupeppen
.tag:nth-of-type(4n+1) .tag__link {
color: #ffd560;
}
.tag:nth-of-type(4n+2) .tag__link {
color: #ee4266;
}
.tag:nth-of-type(4n+3) .tag__link {
color: #9e88f7;
}
.tag:nth-of-type(4n+4) .tag__link {
color: #54d0ff;
}
Das Farbschema hierfür wurde direkt vom Chris' Blogroll übernommen, wo jeder vierte Tag beginnend mit Tag eins gelb ist, jeder vierte Tag beginnend mit Tag zwei rot ist, jeder vierte Tag beginnend mit Tag drei lila ist und jeder vierte Tag beginnend mit Tag vier blau ist.

Dann setzen wir die Fokus- und Hover-Zustände für jeden Link
.tag:nth-of-type(4n+1) .tag__link:focus,
.tag:nth-of-type(4n+1) .tag__link:hover {
box-shadow: inset 0 -1.3em 0 0 #ffd560;
}
.tag:nth-of-type(4n+2) .tag__link:focus,
.tag:nth-of-type(4n+2) .tag__link:hover {
box-shadow: inset 0 -1.3em 0 0 #ee4266;
}
.tag:nth-of-type(4n+3) .tag__link:focus,
.tag:nth-of-type(4n+3) .tag__link:hover {
box-shadow: inset 0 -1.3em 0 0 #9e88f7;
}
.tag:nth-of-type(4n+4) .tag__link:focus,
.tag:nth-of-type(4n+4) .tag__link:hover {
box-shadow: inset 0 -1.3em 0 0 #54d0ff;
}
Ich hätte zu diesem Zeitpunkt wahrscheinlich eine benutzerdefinierte Variable für die Farben erstellen können – wie --yellow: #ffd560, usw. – aber ich habe mich für den Langform-Ansatz für IE 11-Unterstützung entschieden. Ich liebe den box-shadow Hover-Effekt. Es ist eine sehr geringe Code-Menge, um etwas visuell Ansprechenderes zu erreichen als eine Standard-Unterstreichung oder ein unterer Rahmen. Die Verwendung von em-Einheiten hier bedeutet, dass wir eine gute Kontrolle darüber haben, wie groß der Schatten im Verhältnis zum Text ist, den er abdecken muss.
Okay, lassen Sie uns das abrunden, indem wir jeden Tag-Link beim Hover schwarz machen
.tag:nth-of-type(4n+1) .tag__link:focus,
.tag:nth-of-type(4n+1) .tag__link:hover,
.tag:nth-of-type(4n+2) .tag__link:focus,
.tag:nth-of-type(4n+2) .tag__link:hover,
.tag:nth-of-type(4n+3) .tag__link:focus,
.tag:nth-of-type(4n+3) .tag__link:hover,
.tag:nth-of-type(4n+4) .tag__link:focus,
.tag:nth-of-type(4n+4) .tag__link:hover {
color: black;
}
Und wir sind fertig! Hier ist das Endergebnis
Schönes Tutorial, aber ich habe eine Frage.
Ist im folgenden Teil des Codes das else überhaupt erforderlich?
Was würde passieren, wenn es auf nur Folgendes vereinfacht würde?
Gut erkannt, Wiktor. Es sieht so aus, als ob das else dort vielleicht nicht erforderlich ist.
Ich liebe Tag-Clouds, ich dachte, ich wäre der Letzte!
Anstelle eines statischen Mindestwerts für die Schriftgröße verwende ich eine
log()-Funktion der Anzahl der Elemente. Ich finde, sie verringert besser die Lücke zwischen vielen wenig genutzten Tags und wenigen oft genutzten.Hier ist mein Ergebnis
https://nicolas-hoizey.com/tags/
Die Farbe basiert ebenfalls auf der Anzahl der Elemente.
Hier verwende ich die
log()-Funktionhttps://github.com/nhoizey/nicolas-hoizey.com/blob/master/src/_11ty/getTags.js#L33-L39
Und das Nunjucks-Makro, das das Ergebnis verwendet, in meinem Eleventy-Build
https://github.com/nhoizey/nicolas-hoizey.com/blob/master/src/_includes/macros/tagsCloud.njk
Hilft das?
Hallo Nicolas,
Das sieht sehr ordentlich aus, gut gemacht. Danke für die Information.
Ich mag diesen Inline-Effekt, aber leider hat er einige Probleme, wenn es zu einem Zeilenumbruch kommt. Ich gehe davon aus, dass die 1,3em etwas willkürlich gewählt sind, um mit der gegebenen Zeilenhöhe gut zu funktionieren?
Auf jeden Fall verursacht es Probleme – Sie erhalten einen unglücklicherweise unleserlichen Effekt, bei dem die obere Hälfte des Wortes auf Handys und schmaleren Bildschirmen vollständig verdeckt ist. Ideen zur Behebung wären großartig!
Hallo Mg,
Guter Fund. Das ist mir selbst nicht aufgefallen.
Ich schätze, Sie könnten den
box-shadowentfernen und durch einenlinear-gradientersetzen, das könnte funktionierenbackground-image: linear-gradient(to top, #ee4266 90%, transparent 90%)
}
Gibt es eine Möglichkeit, die wiederholte Auflistung derselben Wörter in der Json-Datei zu ändern?
},
Wird zu
},
oder so etwas?
Die wiederholte Auflistung der Wörter erhöht die Dateigröße enorm.
Danke!
Frank