Prof. Dr.-Ing. Oliver Radfelder
Informatik / Wirtschaftsinformatik
Hochschule Bremerhaven
Kurz und Knapp: Javascript

Loggen Sie sich auf hopper ein. Gehen Sie in Ihr Webverzeichnis /var/www/html/IHR-ACCOUNT/ und öffnen Sie den Editor vim mit dem Dateinamen javascriptexample.html.

$ vim javascriptexample.html
und geben Sie langsam und mit korrekter Formatierung den folgenden HTML-Code ein:
<!doctype html>
<html>
  <head>
    <title>Javascript-Seite</title>
    <meta charset='utf-8'>
  <script>
  //hier steht der Javascript Code ...
  </script>
  </head>
  <body>
  <h1 id='myheader'>Javascript-Seite</h1>
  </body>
</html>

Dann gehen Sie mit Ihrem Browser auf https://informatik.hs-bremerhaven.de/IHR-ACCOUNT/javascriptexample.html

Für die folgenden Beispiele gilt, dass der gezeigte Code innerhalb des Script-Tags steht. Javascript-Code innerhalb eines solchen Tags wird während des Ladens ausgeführt. Öffnen Sie in Ihrem Browser (Linux/Windows: F12, MacOS: Option+Command+i ) die DevTools und dort die Konsole - sie dient als einfaches Ausgabeinstrument.

Tragen Sie nun innerhalb des script-Tags eine Zeile Code ein:

  console.log("hello world")

Laden Sie (mit Strg-F5) die Seite nun neu und Sie sehen, dass in dem Konsolen-Fenster hello world! ausgegeben wird. Die Anweisung console.log(...) entspricht also in etwa echo in PHP oder der Bash oder System.out.println() in Java. Das vordefinierte Objekt console ist in jeder Javascript-Umgebung verfügbar und log(...) ist eine Methode des Objektes, der Logmeldungen übergeben werden können. Die Ausgabe ist vor allem für Debugging-Zwecke hilfreich - in realen Anwendungen wird man mit Javascript das Dokument selbst verändern. Dazu später mehr.

In Javascript gibt es - wie üblich - Variable, Schleifen, Bedingungen, Arrays und Funktionen:

let zahl = 7
let name = 'oliver'
let einarray = ['eins','zwei','drei']
for (let i=0; i<einarray.length; ++i) {
  console.log('element '+ i + ': ' + einarray[i])
}
function counter(max){
  let j=0
  while(j<max){
    j++
    if ( j==2 ) {
      console.log("bei zwei")
    }
  }
}
counter(5)
      

Arrays - die in Javascript dynamisch sind - und Objektstrukturen werden mit Literalen erzeugt:


// Arrays
let arr = ['eins','zwei','drei']
arr[3] = 'vier'
arr.push('fuenf')  // hinten anfuegen
arr.pop() // hinten entfernen
arr.unshift('null')  // vorne anfuegen
arr.shift()  // vorne entfernen
for(let i=0;i<arr.length;++i)
   console.log(arr[i])
// Objekte
let obj = {}
obj.vorname = 'Peter'
obj.nachname = 'Petersen'

      

Funktionen sind Objekte erster Ordnung

Eine Besonderheit gibt es allerdings: In Javascript sind Funktionen Objekte erster Ordnung. Das bedeutet, sie lassen sich beispielsweise Variablen zuweisen und später über den Variablennamen aufrufen:

function meineFunktion(arg){
  console.log("in einer Funtion mit Argument: "+arg)
}

meineFunktion("normal")   //normaler Aufruf

let fkt = meineFunktion   //Zuweisung an Variable
fkt("indirekt")

      

Damit lassen sich recht gut typische Callback Strukturen aufbauen:

function meineFunktion(fkt){
  fkt("einWert")
}

function meinCallback(val){
  console.log(val)
}

meineFunktion(meinCallback)  //Achtung: keine Klammern ...

//auch als *anonyme* Funktion mitten im Funktionsaufruf ...
meineFunktion(function(val) { 
                console.log(val)
              })
      

Das mag zunächst ungewohnt und abstrakt aussehen - allerdings arbeitet praktisch jeder Javascript-Code mit diesem Mechanismus. So lässt sich beispielsweise eine Funktion definieren, die einmal pro Sekunde aufgerufen wird, indem sie an die (vordefinierte) Funktion setInterval übergeben wurde:

function gibDatumAus(){
  console.log("Es ist: " + Date.now())
}

window.setInterval(gibDatumAus,1000)

      

Das Objekt window ist ebenfalls in einer Browser-Umgebung immer verfügbar.

In Javascript wird stark Event-basiert programmiert: das bedeutet, dass die Umgebung (der Browser) bestimmte Ereignisse verwaltet. Tritt das Ereignis ein, wird der dafür hinterlegte Code ausgeführt. So hängt man sich meist recht früh an das Ereignis "das-Html-Dokument-wurde-vollständig-geladen" an, indem die Objekteigenschaft onload des window-Objektes mit einer Funktionsreferenz belegt wird. Eigenschaften von Objekten werden ebenfalls über die bekannte Punktnotation angesprochen. So wird die Eigenschaft onload mit einer Funktionsreferenz belegt:

let start = Date.now()

function fertigGeladen(event){
  console.log("Das Dokument ist fertig geladen "+event)
  let jetzt = Date.now()
  console.log("... es hat "+ ( jetzt-start ) +"ms benötigt")
}

window.onload=fertigGeladen
      

Was uns zu dem dritten vordefinierten Objekt bringt: document. Dieses Objekt lässt uns auf das dargestellte Dokument und seine Baumstruktur zugreifen; und zwar zumeist mit dem Aufruf von getElementById(...). Vielleicht ist Ihnen aufgefallen, dass das h1-Element in dem obigen HTML-Dokument noch ein Attribut namens id mit dem Wert myheader hat. Damit können wir uns das Element holen und dessen Eigenschaft innerHTML dann mit einem neuen Wert belegen:

function setHeader() {
  let myheader = document.getElementById("myheader")
  myheader.innerHTML = "Es ist: "+Date.now()
} 
function fertigGeladen(event){
  window.setInterval(setHeader,1000)
}

window.onload=fertigGeladen
      

Der obige Ausschnitt ist schon recht knackig - überlegen Sie einmal, weshalb hier der setInterval-Aufruf erst in dem onload-Callback auftritt.

Javascript und new

Bisweilen sieht man in Javascript den new-Operator, der allerdings anders als in Java oder C++ funktioniert.

Eine Funktion kann auch wie eine Vorlage für komplexere Objekte genutzt werden, wird dann aber üblicherweise groß geschrieben. Grundsätzlich lässt sich jede Funktion auch mit new aufrufen, und dann wird mit Prototypen gearbeitet, wobei dann sogar auch this nutzbar ist. Da die Mechanik aber doch sehr anders als bei Java und Co funktioniert, will ich es hier bei einem kleinen Beispiel belassen.

function Foo(bar1, bar2) {
  this.bar1 = bar1;
  this.bar2 = bar2;
}
let myFoo = new Foo("Bar 1", 2024);
let myFoo = new Foo("Bar 2", 2024);
console.log(myFoo.bar1)

Es wird noch etwas dauern, bis Ihr selber new, class und das Konzept der Prototypen benötigt. Im Moment werdet Ihr eher ab und an ein vordefiniertes Objekt mit new erzeugen (Date, XMLHttpRequest, ...)

Wann Sie new benötigen und wann Sie lediglich eine Funktion aufrufen, die ein Objekt zurückliefert, ergibt sich mit der Zeit und mit der Gewohnheit.

Näheres zu new unter developer.mozilla.org.

Ajax

Mit diesen Mitteln lässt sich bereits ein minimaler Ajax-Aufruf durchführen - die Grundlage des Web 2.0 schlechthin.

let req = new XMLHttpRequest()
function angekommen(){
  if ( req.readyState == 4 && req.status == 200 ) {
    console.log(req.responseText)
  }
}
req.onreadystatechange = angekommen
req.open("GET","eintext.txt")
req.send()
      

In dem obigen Code wird zunächst ein XMLHttpRequest-Objekt erzeugt und der Variablen req zugewiesen. Dann wird die Funktion angekommen definiert, die aufgerufen werden soll, wenn die Antwort des HTTP-Requests angekommen ist. Damit das auch geschieht, wird die eben definierte Funktion an die Objekteigenschaft onreadystatechange des req-Objektes gebunden. Im vorletzten Schritt wird noch der Request geöffnet mit Angabe, ob GET oder POST genutzt werden soll und es wird das angeforderte Dokument übergeben (denken Sie daran, wir befinden uns immer noch in der HTTP-Welt). Nun ist alles vorbereitet und der Request kann mit send() abgeschickt werden.

Der obige Code funktioniert als Minimalbeispiel, hat aber noch ein gewichtiges Problem: Ajax steht für Asynchronous Javascript And XML. Deutlich wird das, wenn Sie nach dem send-Aufruf direkt einen console.log()-Aufruf einfügen und feststellen, dass die Ausgabe vor der Antwort erscheint. Das ist auch gut und richtig und sollte so sein. Allerdings haben Sie jetzt das Problem, dass Sie nicht zwei parallele Ajax-Aufrufe durchführen können, weil die Variable req global ist. Das ließe sich auch anders lösen, aber Javascript hat dafür eine weitere Anlehnung aus der funktionalen Programmierung zu bieten: Closures. Wenn Sie innerhalb einer Funktion eine Funktion definieren, wird der momentane Zustand der in der äusseren Funktion definierten lokalen Variablen festgehalten.

function meineFunktion(arg){
  let aeussereVariable=arg
  function oncePerSecond(){
    console.log(aeussereVariable)
  }
  window.setInterval(oncePerSecond,1000)
}

meineFunktion("eins")
meineFunktion("zwei")
      

Wie man sieht, wird die Funktion als Referenz an setInterval in der Funktion meineFunktion übergeben. Danach ist die Abarbeitung des Funktionskörpers beendet. Allerdings kann später noch auf die (vormals) belegte Variable aeussereVariable zugegriffen werden. Wer viel Java und C++ gewohnt ist, für den oder die ist das ein ungewöhnliches Konzept - und sicherlich beim ersten Ansehen noch nicht in all der Mächtigkeit zu erfassen. Je mehr man aber damit programmiert, desto gewohnter wird es, so zu denken - und Javascript versteht möglicherweise erst richtig, wer sich das auch in eigenem Code zu Nutze macht.

Jedenfalls ist das der Grund, weshalb die meisten Ajax-Beispiele, die Sie im Web finden, eher so aussehen:

function load(doc,logStatement) {
  let req = new XMLHttpRequest()
  req.onreadystatechange = function() {
    if (req.readyState == 4 && req.status == 200) {
     console.log(logStatement+":"+req.responseText)
    }
  }
  req.open("GET", doc )
  req.send()
}
load("eintext.txt","eintext.txt kam an")
load("einanderer.txt","kam auch an")
      

Nun haben wir das Problem nicht mehr - wir haben keine globale Variable und wir können beliebig viele Dokumente asynchron anfordern - das als zweites Argument übergebene Logstatement wird immer das richtige sein. (Wiederholen Sie das Laden mit STRG-F5 mehrmals und schauen Sie, welches zuerst ankommt.)

Und wenn Sie jetzt mutig sind und versuchen wollen zu verstehen, ob Sie verstanden haben, übergeben Sie doch mal eine Funktion als zweites Argument, die dann aufgerufen wird, wenn der Response angekommen ist. Das ist dann wirklich modernes Event-basiertes, funktionales Programmieren (und im Kern etwa 60 Jahre alt ...)

Das Dokument, das Sie mit einem Ajax-Request anfordern, kann natürlich auch ein php- oder cgi-Request sein. Probieren Sie es aus.

Ein GET-Request darf bekanntlich gecached werden - für unsere Anwendungen nicht unbedingt förderlich... Wenn Sie ein Neuladen mit jedem Request erzwingen wollen, müssen Sie dafür sorgen, dass die URL jedesmal anders aussieht. Ein nicht unübliches Verfahren ist, nach dem Fragezeichen die aktuelle Zeit (in Millisekunden) als Parameter mitzugeben:

  ...
  req.open("GET", doc+"?time="+Date.now() )
  ...
      

Alternativ zu dem Mechanismus mit onreadystatechange gibt es noch die etwas modernere Variante mit onload und gegebenenfalls mit onerror, wobei diese Funktion nur einmalig bei erfolgreichem Response aufgerufen wird, was sich etwas kürzer liest:

function load(doc,logStatement) {
  let req = new XMLHttpRequest()
  req.onload = function(e) {
    let xhr = e.target
    console.log(logStatement+":"+xhr.status)
  }
  req.open("GET", doc )
  req.send()
}
load("eintext.txt","eintext.txt kam an")
      

Dazu sieht man, dass statt das XMLHttpRequest aus dem äußeren Context zu nehmen, es etwas klarer ist, es sich innerhalb der onload-Funktion von dem übergebenen Event-Objekt zu holen. Das geht ebenfalls bei der onreadystatechange-Variante.

Wenn Sie Daten zwischen Ihrer php-, cgi- oder java-Anwendung und Javascript übergeben wollen, nutzen Sie in Javascript die Funktionen JSON.stringify(obj) und JSON.parse('...'). Auf php-Seite nutzen Sie json_encode($obj) und json_decode('...'). So können Sie Objektstrukturen (z.B. assoziative Arrays in php) vom Server per Ajax holen und als Javascript-Objekte nutzen.

Websockets

Um Websockets zu nutzen, brauchen Sie auf der Serverseite einen Websocket-Server, der auf einem eigenen Port lauscht. Bei uns ist websocketd installiert, den Sie mit:

websocketd --port 3000 meinskript.sh

starten können. Der Port 3000 wird als Webpfad über https://informatik.hs-bremerhaven.de/docker-IHRACCOUNT-websocket/ erreicht. Das Skript wird für jeden Connect aufgerufen und liest von Stdin Nachrichten und schreibt sie über Stdout raus.

#!/usr/bin/env bash
while read -r line; do 
  echo "[$line]"
  echo "[$line]" >&2
done 

Auf der HTML-Seite brauchen Sie den entsprechenden Javascript-Code:

let ws = new WebSocket('wss://informatik.hs-bremerhaven.de/docker-demo-websocket/')
console.log("try...")
ws.onopen=function (e) {
  console.log('connected')
  e.target.send("eins")
}
ws.onmessage=function (e) {
  document.getElementById("myheader").innerHTML=e.data
}
ws.onclose=function(e){
  console.log("closed")
}

Jetzt haben Sie - ähnlich einer TCP-Verbindung - die Möglichkeit, auf beiden Seiten jederzeit zu schreiben oder zu lesen.

Grafik mit dem Canvas

Mit dem Canvas-Element haben Sie in der Kombination von html und Javascript heutzutage die Möglichkeit, Grafiken und Animationen zu erstellen. Zunächst benötigen Sie in dem HTML-Code ein <cavas>-Element, das Sie mit einer ID versehen und sich wieder mit getElementById holen können.

let canvas=document.getElementById('mycanvas')
let context=canvas.getContext('2d')
context.clearRect(0,0,canvas.width,canvas.height)
context.fillStyle='rgb(200,0,0)'
context.fillRect(10,10,100,100)
context.strokeStyle='rgb(0,255,0)'
context.strokeRect(5,5,105,105)
context.lineWidth=3
context.beginPath()
context.moveTo(0,0)
context.lineTo(100,100)
context.stroke()
context.font = '48px serif'
context.fillText('Hello World',10,50)

Mit dem Funktionsaufruf requestAnimationFrame(fct) wird ein Neuzeichnen des Browsers angefordert. Übergeben wird eine Funktionsreferenz und der Browser wird versuchen, mit mindestens 60 fps die Bildrate animationsfreundlich zu halten.

function render(){
  let context ... // wie oben
  requestAnimationFrame(render)
}
window.onload=function(){
  // init
  requestAnimationFrame(render)
}

Für hochauflösende Displays ist es nicht ganz trivial, mit den CSS-Größen des Canvas und den DevicePixeln zu arbeiten. Das window-Objekt hat eine Eigenschaft mit dem Namen devicePixelRatio, mit dem sich bei Erzeugung oder regelmäßig die korrekte Größe berechnen lässt.

window.onload=function(){
  // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas?retiredLocale=de
  // Get the DPR and size of the canvas
  let dpr = window.devicePixelRatio
  let canvas = document.getElementById("mycanvas")
  let rect = canvas.getBoundingClientRect()

  // Set the "actual" size of the canvas
  canvas.width = rect.width * dpr
  canvas.height = rect.height * dpr
  console.log("width:"+canvas.width)

  // Scale the context to ensure correct drawing operations
  let ctx = canvas.getContext("2d")
  ctx.scale(dpr, dpr)

  // Set the "drawn" size of the canvas
  canvas.style.width = `${rect.width}px`
  canvas.style.height = `${rect.height}px`
  requestAnimationFrame(render)
}
function render(ts){
  let canvas = document.getElementById('mycanvas')
  let ctx = canvas.getContext("2d")
  const { width, height } = canvas.getBoundingClientRect();
  ctx.clearRect(0,0,width,height)
  ...
}

Für Weiterführendes schauen Sie unten in die Links.

Sonstiges:

Javascript ist auch auf dem Server möglich - wenn auch seltener zu sehen als im Browser. Bei uns in der Infrastruktur ist die Javascript-Engine node natürlich auf hopper und in Ihren Dockern installiert. Schreiben Sie doch einmal die folgende kleine Schleife in eine Datei namens demo.js in Ihrem Home-Verzeichnis ...

for (let i=0; i<10; ++i){
  console.log("ich bin bei:"+i)
} 

... und lassen Sie sie mit:

$ node demo.js

... laufen. Oder Sie nutzen Ihr Wissen, dass in dem Shebang auch ein Interpreter stehen kann, machen die Datei mit chmod ausführbar und rufen Sie direkt mit ./demo.js auf.

#!/usr/bin/node
for (let i=0; i<10; ++i){
   console.log("ich bin bei:"+i)
}

In der VM installiert man node entweder so, wie es in docker-tools beschrieben ist oder über den Node Version Manager (nvm), wie es weiter unten beschrieben ist. Größere Projekte wird man im Allgemeinen mit npm verwalten, was für sich genommen ein recht großes Thema ist. Zudem gibt es neben node auch noch dino und bun bun-vs-node-vs-deno.

Moderneres Javascript

An einigen Stellen werden Sie etwas modernere Varianten finden.

css Selector

Sie können statt getElementById und getElementsByClassName in aktuellen Browsern querySelector und querySelectorAll nutzen, um vermittels css-Selektoren Elemente zu suchen:

let element = document.querySelector("#myspecialid")
let elementList = document.querySelectorAll(".myimageclass")
//in dem div mit der class mycontent ein input mit dem Namen login:
let special = document.querySelector("div.mycontent input[name='login']")

Template-Strings

Template-Strings werden in Backticks eingeschlossen. Innerhalb lässt sich mit ${..} auf äußere Werte zugreifen und hier sogar Ausdrücke angeben:

let varia = 5;
console.log(`varia: ${varia} 
2*varia: ${2 * varia}.`);

Arrow-Functions

Als Kurzform für Callback-Funktionen (anonym oder nicht) bieten sich bisweilen Arrow-Funktionen bzw. Arrow-Function-Expressions an:

 // keines oder mehr als ein Argument müssen geklammert werden
 // der Ausdruck hinter => wird zurückgegeben
 const adder = (a,b) => a+b*100
 console.log(adder(3+4))
 // wenn nur ein Argument erwartet wird, können die Klammern weggelassen werden
 const simple = a => a*a
 console.log(simple(7))
 // als Funktionen mit body
 const withbody = () => { console.log("nix"); return 7;}

 // insbesondere mit Mapping-Funktionalität sehr schön kompakt
 const arr = [5, 6, 13, 0, 1, 18, 23];
 const sum = arr.reduce((a, b) => a + b);
 const even = arr.filter((v) => v % 2 === 0);
 doub = arr.map((v) => v * 2);
     

Mehr zu lesen dazu unter: Arrow Functions auf Mozilla Developer Network.

Generatoren in Javascript

Generatoren sind in mehreren Programmiersprachen in Mode. Der Grundgedanke ist, dass Funktionen über Aufrufe hinweg ihren Zustand erhalten:

function* generator() {
  yield 1
  yield 2
}

let gen = generator()

console.log(gen.next().value) // 1
console.log(gen.next().value) // 2

Generatoren sind sowohl Iteratoren als auch Iterables:

function* powers(n) {
  //endless loop to generate
  for (let current = n; ; current *= n) {
    yield current
  }
}
for(let p of powers(2){
  if (p > 64) break
}
     
     

Web-APIs

Es gibt einige spezifische APIs. Beispielsweise das DeviceOrientation-API:

function orientation_change(e) {
    let alpha = Math.round(e.alpha/100)*100
    let beta  = Math.round(e.beta/100)*100
    let gamma = Math.round(e.gamma/100)*100
}
window.onload = function() {
    window.addEventListener("deviceorientation", orientation_change, false)
}

npm als Alternative zu installiertem node, npx, ...

Mit nvm können Sie sich unterschiedliche Versionen von node und npm installieren:

wget https://raw.githubusercontent.com/creationix/nvm/master/install.sh 
bash install.sh
source ~/.bashrc
nvm ls-remote
nvm install node
nvm install node --lts
nvm ls
node --version
nvm use --lts
...
nvm use default
mkdir ~/.npm-global
# in .bashrc
export NPM_CONFIG_PREFIX=~/.npm-global

Sonstiges: FetchAPI, FileAPI, GeolocationAPI, WebWorkers, IndexDB, PerformanceAPI, TouchEvents, WebSockets,

Frameworks/Libraries: Angular, NextJS, React, ReactNative, JQuery, Bootstrap, Jest, Bootstrap, Express, Polymer, Lodash, Dojo, D3, Vue, Nuxt, Svelte, Ember, Fastly, Express, Svelte, Pixie, Velocity, Preact, Backbone, Alpine, Mithril, Aurelia, Webpack, Esbuild, Vite, Parcel, ...

Interessant: ThreeJS, Leaflet, puppeteer, k6, playwright, webassembly/wasm...

Was noch fehlt: preventDefault, ES6, Promises, Module, Generators, Async/Await, this, class, map, ... The Modern JavaScript Tutorial

Wenn Sie dieses Tutorial durchgearbeitet haben, schauen Sie doch mal bei Ulrike Erb auf der Webseite vorbei: dort gibt es ein wunderbares Leaflet-Tutorial und ebenfalls eine Einführung in das Programmieren mit Javascript ProgrammierBasicsJavaScript.pdf.

Javascript und Frameworks/Libraries

Die unglaubliche Masse an Frameworks und Libraries im Javascript Umfeld ist so unübersichtlich, dass es unmöglich erscheint, überhaupt einen Weg zu empfehlen von purem Javascript zu einer leichtgewichtigen Lösung, die dennoch zukunftsfähig ist. Das hat sicher auch damit zu tun, dass mit Modulen und node/npm der Deployment-Prozess zunächst vereinfacht wird, dadurch aber wiederum der Technologiestack so komplex wird, dass er kaum durchschaubar ist.

Und weil der dann wieder etwas träge ist, braucht es wieder einen Mechanismus, der Live-Reloading im Dev-Prozess ermöglicht (Vite). Nun will man nicht nur mit Javascript das Backend entwickeln, sondern vielleicht eben auch mit Php, Python oder Java. Dort gibt es natürlich wieder eigene Vorstellung des Dependency-Managements und der Build-Prozesse (maven, gradle, ...). Die wiederum zusammenzufügen, ist nicht immer leicht.

Es gibt immer wieder Versuche, das Chaos aufzuräumen: WebComponents und OpenUI hören sich vielversprechend an, sind aber noch nicht so weit.

JQuery/JQueryUI war der erste, extrem erfolgreiche Versuch, mit in Javascript eingebauten Mechanismen eine angenehme Bibliothek zu bauen. JQuery ist mehr als 20 Jahre alt und lebt noch immer. Zwischenzeitlich hatte es einen eher schlechten Ruf, nun aber lohnt sich vielleicht ein neuer Blick: JQuery 4.0 steht nach acht Jahren kurz vor der Veröffentlichung! Letztlich hat JQuery einige Sachen geboten, die es anfangs in Javascript entweder gar nicht gab (isArray) oder über die sich die Browser nicht einig waren (event order). Das hat sich geändert und nun könnte ernsthaft JQuery der nächste, große Wurf werden ... Ein interessantes Video dazu: BREAKING: jQuery V4 Is Here (YES REALLY).

Dennoch: Ein gutes Video von jemandem, der recht spät merkte, dass er lange nicht wirklich verstanden hat, wie CSS funktioniert und dann vieles neu lernen musste, ist Do You Really Need Bootstrap or Tailwind? .

"In a nutshell: there is no black and white. [...] The only thing you should do is learning the pure version of languages first. If you don't know CSS, don't look for any framework. If you don't know Javascript, do not try to learn React or any other frameworks. Because it makes things harder [...]".

Im Grunde sagt Euch das jede/r, aber die Verlockungen des schnellen Erfolges scheinen so groß zu sein, dass dann doch lieber das schnelle Tutorial genommen wird, mit dem sich eine ToDo-Liste in zwanzig Minuten per Copy-and-Paste erstellen lässt - der sogeannte Happy-Path - dann aber konkrete Anforderungen umzusetzen, erfordert ein tiefes Verständnis - und wenn das nicht da ist, kommt sehr mieser Code heraus.

Wir wollen Euch den Frust ersparen und wollen daher, dass Ihr Javascript und CSS von der Pieke auf lernt. Schließlich studiert ihr Informatik oder Wirtschaftsinformatik und seid nicht in einem Bootcamp, in dem Ihr in ein paar Wochen zum Fullstack-Dev werden sollt ...

Weiterführendes