Kurze Frage, warum sind Umbrüche in JavaScript nicht erlaubt?
In JavaScript sind Umbrüche im Allgemeinen schon erlaubt, von wenigen Ausnahmen abgesehen. Eine dieser Ausnahmen sind gewöhnliche Stringliterale, so wie du sie verwendest.
Wie bereits gesagt wurde, muss ein Zeilenumbruchzeichen in diesem Kontext escaped werden. Dafür wird ein Backslash verwendet, wobei zu beachten ist, dass danach direkt der Zeilenumbruch kommen muss.
const string = 'All human beings are born free \
and equal in dignity and rights.';
Zeilenumbruchzeichen im String werden mit \n
kodiert. Will man also einen Umbruch im String und gleichzeitig einen Umbruch im Quelltext, dann kann man das wiefolgt schreiben:
const string = 'They are endowed with reason and conscience\n\
and should act towards one another\n\
in a spirit of brotherhood.';
Mittlerweile gibt es in JavaScript aber noch eine weitere Form von Strings, nämlich sogenannte Templateliterale, die in Backticks eingeschlossen werden. Bei der Verwendung dieser Strings müssen Zeilenumbrüche nicht kodiert werden:
const string = `Everyone has the right to life, liberty and security of person.
No one shall be held in slavery or servitude`;
Ein weiteres Feature ist, dass man innerhalb eines solchen Literals an beliebiger Stelle Ausdrücke einfügen kann. Also statt wie du in deinem Beispielcode Zeichenketten mit dem überladenen Plusoperator zu verknüpfen, kann man mit Templateliteralen so etwas schreiben:
function getHeading () {
return 'Template Literals';
}
function getText () {
return 'Template Literals are enclosed by backtick characters.';
}
document.body.innerHTML = `
<header>
<h1>${ getHeading() }</h1>
</header>
<main>
<p>${ getText() }</p>
</main>
`;
Zwischen ${
und }
kann irgendein Ausdruck stehen, also zum Beispiel auch ein Funktionsaufruf wie in dem Code oben. Oder man referenziert eine zuvor deklarierte Variable. Oder einen Parameter. Was auch immer. Der Ausdruck wird dann ausgewertet und das Ergebnis in den String eingebaut.
Darüber hinaus besteht die Möglichkeit, ein solches Templateliteral durch eine Funktion selbst weiter zu verarbeiten, statt einfach alle Fragmente in der bestehenden Reihenfolge zusammenbauen zu lassen:
function emphasize ([start, end], content) {
return `${ start }<em>${ content }</em>${ end }`;
}
function output (text) {
document.body.innerHTML = emphasize`<p>${ text }</p>`;
}
output('Liberté, égalité, fraternité');
Wird der obige, einigermaßen sinnfreie Code ausgeführt, ergibt das in etwa folgendes Markup:
<body>
<p><em>Liberté, égalité, fraternité</em></p>
</body>
Eine Referenz auf die zur Weiterverarbeitung ausgewählte Funktion wird als Tag direkt vor dem Templateliteral notiert, in dem Beispiel oben also die Funktion emphasize
. Diese Funktion wird dann automatisch mit 1 + N Argumenten aufgerufen.
Das erste Argument ist ein Array mit den Textfragmenten, alle weiteren Argumente sind die Werte, zu denen die in dem String notierten Ausdrücke aufgelöst wurden. Die Antwort auf die Frage, wieviele Argumente übergeben werden, hängt also von dem verarbeiteten String ab.
function emphasize ([start, end], content) {
return `${ start }<em>${ content }</em>${ end }`;
}
In der Beispielfunktion destrukturiere ich das Array mit den Strings bereits in der Parameterliste, da ich weiß, dass nur zwei Strings übergeben werden, und ich notiere nur einen formalen Parameter für den textuellen Inhalt des Elements.
function tag (strings, ...values) {
// do something
}
Wenn man nicht weiß, was einen erwartet, wäre diese Notation sicher sinnvoller. strings
ist einfach das Array mit den Textfragmenten und in dem Array, das durch den Restparameter mit dem Namen values
erzeugt wird, werden dann alle in dem Literal hinterlegten Werte aufgesammelt.
Erwähnenswert wäre in diesem Zusammenhang noch, dass man statt einen String natürlich auch eine Funktion zurückgeben kann. Als Closure hätte diese Funktion Zugriff auf die lokalen Variablen der Ursprungsfunktion. So können mehrere Verarbeitungsschritte elegant miteinander verknüpft werden.
Der Nachteil von Templateliteralen besteht im Moment leider darin, dass sie noch nicht von allen für den Produktiveinsatz relevanten Browsern unterstützt werden. Ein Transpiler wie Babel kann den Code jedoch entsprechend aufbereiten.
Alles in allem ist Markup im Scriptcode aber ohnehin nicht so sexy, und wenn ich sehe, dass du einen Block HTML im Script vorhälst, bei dem offenbar nur ein einziger Abschnitt dynamisch generiert wird, könnte sich hier vielleicht ein Blick auf das Template-Element lohnen.
<template>
<div>
<label><span>. Artikel</span> <input placeholder="Artikel ID"/></label>
</div>
</template>
und
const template = document.querySelector('template'),
clone = template.content.cloneNode(true); // document.importNode(template.content, true);
clone.querySelector('label').firstElementChild.prepend(x);
wrapper.append(clone);
Mit jQuery ginge das noch kürzer.
Das Template-Element wird zusammen mit dem anderen Markup geparst, aber nicht gerendert. Wenn du das Element referenziert hast, kannst du über die Eigenschaft content
auf ein DocumentFragment mit dem Inhalt des Templates zugreifen, den du wie in dem Beispiel oben beliebig vervielfältigen kannst.
Die nötigen Änderungen, wie das Einfügen der Artikelnummer vor dem textuellen Inhalt des Labels oder die Vergabe einer dokumentweit einzigartigen ID, kannst du dann vergleichsweise einfach vornehmen und das Ganze im Anschluss an der richtigen Stelle ins DOM hängen.
Auf diese Weise hättest du den statischen Teil ins Markup ausgelagert und dein Script wäre vermutlich um einiges schlanker.
Um auch ältere Browser wie den Internet Explorer zu unterstützen, die vom Template-Element noch nie was gehört haben, wäre es aber sicher gut, ein Polyfill einzubinden.
Wie auch immer, das war jetzt größtenteils an der eigentlichen Fragestellung vorbei, aber vielleicht kannst du oder jemand der mitliest ja was damit anfangen. ;-)