JavaScript Einbinden von HTML führt zu JS-Fehler

Photon

Commodore
Registriert
Apr. 2006
Beiträge
5.036
Hallo Community,

eins vorweg, ich habe keinerlei Erfahrung mit HTML und JS, bin aber in einer Situation, wo ich etwas zusammenschustern soll, das irgendwie funktioniert. Deshalb bin ich bisher so vorgegangen, wie ich in solchen Fällen immer vorgehe: Google anschmeißen und aus Manuals und Foren copy&pasten, bei Problemen versuchen zu verstehen, wie es funktionieren soll und warum es das nicht tut. Nach vielen Problemen und Fehlern, die ich bisher auf diese Art lösen konnte, hänge ich nun an einem, das mit dieser Methode wohl nicht zu lösen ist.

Das Problem ist folgendes: Ich möchte viele gleichartige Seiten haben, die alle gleich aufgebaut sind, der einzige Unterschied ist der Inhalt einer json-Datei, die im Header eingebunden wird. Nun würde ich gerne so etwas realisieren:

HTML:
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Header einbinden, für alle Seiten gleich -->
    {% include ../includes/head.html %} 
    <!-- Daten einbinden, für jede Seite anders -->
	<script language="javascript" type="text/javascript" src="data.json"></script>
  </head>
  <body  onload="init();">
    <!-- Body einbinden, für alle Seiten gleich -->
    {% include ../includes/body.html %}
  </body>
</html>

Auf diese Art könnte ich Header und Body in den entsprechenden Dateien anpassen, ohne viele Dateien bearbeiten zu müssen.

Das Problem ist: Es funktioniert nicht. :) Es wird einfach der Text

Code:
{% include ../includes/head.html %} {% include ../includes/body.html %}

auf einem weißen Hintergrund angezeigt. Den Code-Schnippsel habe ich übrigens aus der ersten Antwort hier: http://stackoverflow.com/questions/22326742/server-side-includes-alternative

Die anderen Antworten habe ich auch schon alle durch. Zum Beispiel der Vorschlag in der letzten Antwort (wo erst mal nur der Body ausgelagert wird):

HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<!-- CSS Files -->
<link type="text/css" href="../css/base.css" rel="stylesheet" />
<link type="text/css" href="../css/Spacetree.css" rel="stylesheet" />

<!--[if IE]><script language="javascript" type="text/javascript" src="../../Extras/excanvas.js"></script><![endif]-->

<!-- JIT Library File -->
<script language="javascript" type="text/javascript" src="../includes/jit.js"></script>

<!-- Graph tree data -->
<script language="javascript" type="text/javascript" src="testgraph.json"></script>

<!-- Example File -->
<script language="javascript" type="text/javascript" src="../includes/graph.js"></script>

</head>

<body onload="init();">
    <!-- Content, inclusion from http://stackoverflow.com/questions/35249827/can-you-link-to-an-html-file -->
    <div w3-include-html="../includes/body.html"></div> 
    <script>
(function () {
  myHTMLInclude();
  function myHTMLInclude() {
    var z, i, a, file, xhttp;
    z = document.getElementsByTagName("*");
    for (i = 0; i < z.length; i++) {
      if (z[i].getAttribute("w3-include-html")) {
        a = z[i].cloneNode(false);
        file = z[i].getAttribute("w3-include-html");
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
          if (xhttp.readyState == 4 && xhttp.status == 200) {
            a.removeAttribute("w3-include-html");
            a.innerHTML = xhttp.responseText;
            z[i].parentNode.replaceChild(a, z[i]);
            myHTMLInclude();
          }
        }      
        xhttp.open("GET", file, true);
        xhttp.send();
        return;
      }
    }
  }
})();
</script>
</body>
</html>

Dort funktioniert mehr: Die Webseite wird grundsätzlich angezeigt, aber die im Header eingebundenen .js-Dateien werfen Fehler:

http://pix.toile-libre.org/upload/original/1483181736.png

Spannend ist, dass der Fehler nicht in 100% der Fälle kommt, manchmal (selten) funktioniert alles aus irgendeinem Grund. Das ist für mich völlig unerklärlich und mysteriös...

Hier ist, wie die Seite eigentlich aussehen sollte: https://serlo-graphs.github.io/JIT/Graphs/testgraph.html
Und hier ist, was passiert, wenn ich versuche den Body auszulagern: https://serlo-graphs.github.io/JIT/Graphs/testgraph_ausgelagert.html

Ich würde am liebsten ein Minimalbeispiel präsentieren, bei dem das Problem auftaucht, da der Fehler aber in den JS-Libraries verwurzelt zu sein scheint, ist mir das bisher nicht gelungen. Wäre super, wenn sich jemand das Problem trotzdem anschauen und hilfreiche Tipps geben könnte! :)

Viele Grüße und einen guten Rutsch ins neue Jahr!
Photon
 
Zuletzt bearbeitet:
Danke für die Vorschläge! Ich versuche eigentlich größere Frameworks zu vermeiden, da die Seite wirklich einfach ist und bis auf das Auslagern der statischen Inhalte schon mit einfachem HTML funktioniert. Aber wenn es nicht anders geht, wäre natürlich die Nutzung der von dir vorgeschlagenen Frameworks grundsätzlich denkbar. Gibt es da Codebeispiele, die ich nach der üblichen Copy&-Paste-Methode gleich ausprobieren könnte? Und welches der Vorschläge wäre der am meisten geeignete Kandidat? Und, last but not least, ist zu erwarten, dass der von mir beschriebene mysteriöse js-Fehler mit ihnen nicht auftaucht?

edit: Und, ganz wichtig, laufen solche Dinger denn auch auf github.io?
 
Das Problem bei Deinem ersten Ansatz ist wahrscheinlich die Verzeichnisstruktur und wo Du die Include-Dateien ablegst. Da auf Github Jekyll verwendet wird, solltest Du auch die Jekyll Verzeichnisstruktur verwenden: http://jekyllrb.com/docs/structure/

Dort legst Du alles, was Du in Deinem Script in includes hast folglich in _includes ab und % include bezieht sich automatisch auf dieses Verzeichnis. Jede Pfadangabe in % include ist also relativ zu diesem _includes Verzeichnis.

Damit sollte es eigentlich funktionieren.
 
die ganzen sachen erzeugen statisches html.. nichts serverseitiges.. laufen also auf github.io

das einfachste wäre wahrscheinlich hexo oder jigsaw..

alternativ habe ich gerade versucht das umzusetzen was du umsetzen wolltes.. glaub ich :D

kannst ja mal testen

index.html
HTML:
<html>
    <head>
        <title>yolo</title>
    </head>
    <body>
        <div class="include" data-include="include.html"></div>
    </body>
    <script>
    Array.from(document.querySelectorAll('.include')).forEach(item => {
        fetch(item.getAttribute('data-include')).then(response => {
            return response.text()
        }).then(html => {
            item.innerHTML = html
        })
    })
    </script>
</html>

include.html
HTML:
<strong>swag</strong>

vll hilft das ja.. da ganze muss auf einem webserver laufen.. weil fetch sonst wegen cors meckert
 
@Balou72: Hmm, bin nicht sicher, dass Jekyll bei mir überhaupt läuft, muss man das noch irgendwie aktivieren? Habe versucht einen Ordner _includes, in dem die Dateien body.html und head.html liegen, an unterschiedliche Stellen zu schieben (Hauptverzeichnis, gleiches Verzeichnis wie die HTML-Datei, ...) aber es wird unverändert die einsame Zeile mit den Einbindungsbefehlen angezeigt.

@kling1: Danke! Hier ist, was mit deinem Vorschlag passiert: https://serlo-graphs.github.io/JIT/Graphs/testgraph_ausgelagert4.html Die js-Datei wird also irgendwie nicht richtig ausgeführt, allerdings kommt im Debugger interessanterweise keine Fehlermeldung, das verstehe ich überhaupt nicht...
Ergänzung ()

Jetzt habe ich ein wirklich minimales Minimalbeispiel. ;)

Funktionierendes HTML ohne Auslagerung des Bodys:

HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><head>
<script language="javascript" type="text/javascript" src="minimal.js"></script>
</head>
<body onload="init();">

<div id="demo">
2+2=5
</div>

</body>
</html>

Ausprobierbar unter https://serlo-graphs.github.io/JIT/Testing/minimalbeispiel_komplett.html

Die eingebundene JS-Datei:

Code:
function init(){document.getElementById('demo').innerHTML = '2+2=4';}

Nun die nicht funktionierenden Beispiele mit ausgelagertem Body (die JS-Datei ist dieselbe):

Zunächst die ausgelagerte Body-Datei:

HTML:
<div id="demo">
2+2=5
</div>

Und die nicht funktionierenden Varianten sie einzubinden:

HTML:
<!DOCTYPE html>
<html>
<head>
<script language="javascript" type="text/javascript" src="minimal.js"></script>
</head>
<body onload="init();">
	<div class="include" data-include="body_minimal.html"></div>
</body>
<script>
Array.from(document.querySelectorAll('.include')).forEach(item => {
	fetch(item.getAttribute('data-include')).then(response => {
		return response.text()
	}).then(html => {
		item.innerHTML = html
	})
})
</script>
</html>

ausprobierbar unter https://serlo-graphs.github.io/JIT/Testing/minimalbeispiel.html und

HTML:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<script language="javascript" type="text/javascript" src="minimal.js"></script>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="" onload="init();">

<div ng-include="'body_minimal.html'"></div>

</body>
</html>

ausprobierbar unter https://serlo-graphs.github.io/JIT/Testing/minimalbeispiel3.html sowie

HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script language="javascript" type="text/javascript" src="minimal.js"></script>
</head>

<body onload="init();">
    <!-- Content, inclusion from http://stackoverflow.com/questions/35249827/can-you-link-to-an-html-file -->
    <div w3-include-html="body_minimal.html"></div> 
    <script>
(function () {
  myHTMLInclude();
  function myHTMLInclude() {
    var z, i, a, file, xhttp;
    z = document.getElementsByTagName("*");
    for (i = 0; i < z.length; i++) {
      if (z[i].getAttribute("w3-include-html")) {
        a = z[i].cloneNode(false);
        file = z[i].getAttribute("w3-include-html");
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
          if (xhttp.readyState == 4 && xhttp.status == 200) {
            a.removeAttribute("w3-include-html");
            a.innerHTML = xhttp.responseText;
            z[i].parentNode.replaceChild(a, z[i]);
            myHTMLInclude();
          }
        }      
        xhttp.open("GET", file, true);
        xhttp.send();
        return;
      }
    }
  }
})();
</script>
</body>
</html>

ausprobierbar unter https://serlo-graphs.github.io/JIT/Testing/minimalbeispiel4.html

Bei den nicht funktionierenden Beispielen mit ausgelagertem HTML meldet der JS-Debugger:

Code:
TypeError: document.getElementById(...) is null

Wie ist das zu verstehen?
 
Zuletzt bearbeitet:
Probiere in Deinem Minimal-Beispiel 3 mal folgende Änderung aus:
Code:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
     
<script language="javascript" type="text/javascript" src="minimal.js"></script>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="">
     
<div ng-include="'body_minimal.html'" onload="init()"></div>
     
</body>
</html>

Denn wenn das onload im body-tag bleibt, dann wird die init-Funktion schon aufgerufen, bevor Angular den ausgelagerten Body eingefügt hat, somit gibt es noch kein Element mit der id "demo", daher dann die Fehlermeldung.

Durch die Änderung sollte die init-Funktion erst aufgerufen werden, nachdem Angular das Include eingefügt hat.

Ganz allgemein: Versuche es zu vermeiden den body onLoad Event-Listener zu nutzen. Nutze stattdessen lieber die Möglichkeiten von DOMContentLoaded. Suche einfach mal danach. Mit Hilfe dieses Ereignisses kannst Du HTML und JS Code gut trennen. Einziger Nachteil (wenn man das überhaupt noch als Nachteil sehen kann): IE < 9 unterstützt dieses Ereignis nicht.
 
Zuletzt bearbeitet:
Danke für den Tipp! Die Verschiebung des onload-Tags bringt leider keine Besserung: https://serlo-graphs.github.io/JIT/Testing/minimalbeispiel3.html Habe auch schon mal versucht, onload in die js-Datei zu packen, also aus der Hauptdatei ganz rauszunehmen und in der js-Datei zu schreiben

Code:
window.onload = function init(){document.getElementById('demo').innerHTML = '2+2=4';}

aber leider ohne Änderung...
Ergänzung ()

Ich habe eine ziemlich dumme aber funktionierende Lösung gefunden. Aber wie es so schön heißt: Wenn es dumm aussieht aber funktioniert, ist es nicht dumm. :)

Habe den Inhalt des Bodys mit document.write() in eine externe .js-Datei geschrieben:

HTML:
<!DOCTYPE html>
<html>
<head>
<script language="javascript" type="text/javascript" src="script.js"></script>
</head>
<body>
<script language="javascript" type="text/javascript" src="body.js"></script>
</body>
</html>

Code:
//body.js
document.write('<div id="demo"->2+2=5</div>');

Code:
//script.js
document.getElementById('demo').innerHTML = '2+2=4';

Das einzige Problem ist, dass man jede Zeile mit einem Backslash beenden muss, also wenn der Body etwas umfangreicher ist, sieht es furchtbar aus und das Code-Highlighting in Editoren sieht das ganze HTML-Zeug als String...
 
Zuletzt bearbeitet:
Also ich würde das ja so umsetzen:
https://pcprofil.de/photon

index.html
Code:
<!DOCTYPE html>
<html>
<head>
    <script>
        document.addEventListener("DOMContentLoaded", function(e) {
            Array.from(document.querySelectorAll(".include")).forEach(item => {
            fetch(item.getAttribute("data-include")).then(response => {
                return response.text();
            }).then(html => {
                item.innerHTML = html;

                // load minimal.js
                var js = document.createElement("script");
                js.src = "minimal.js";
                document.body.appendChild(js);
            });
        });
    });
    </script>
</head>
<body>
    <div class="include" data-include="body_minimal.html"></div>
</body>
</html>

body_minimal.html
Code:
<div id="demo">
    Ich bin das Demoelement!
</div>

minimal.js
Code:
alert(document.getElementById("demo"));

Die JS-Datei (minimal.js hier in diesem Fall) kann leider erst geladen werden, wenn die body_minimal.html geladen wurde, da sie ja auf Elemente innerhalb dessen zugreifen möchte.

Um auf dein Ursprungsproblem zurückzukommen: Machst du denn mit der JSON-Datei auch etwas im Body? Du könntest ja für den Header überall die selbe JS-Datei angeben, nur eben den String "minimal.js" nicht hardcoden, sondern im <head> als <meta> tag oder im body als <span> hinterlegen.
 
Zuletzt bearbeitet:
Ändert sich deine graph.js denn? Ja, mein Vorschlag würde (denke ich) funktionieren.
 
Was meinst du denn mit "sich ändern"? :)

Habe folgendes Minimalbeispiel versucht:

HTML:
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<script>
		document.addEventListener("DOMContentLoaded", function(e) {
			Array.from(document.querySelectorAll(".include")).forEach(item => {
			fetch(item.getAttribute("data-include")).then(response => {
				return response.text();
			}).then(html => {
				item.innerHTML = html;
 
				// load minimal.js
				var js = document.createElement("script");
				js.src = "minimal.js";
				document.body.appendChild(js);
			});
		});
	});
	</script>
</head>
<body>
	<div class="include" data-include="body_minimal.html"></div>
</body>
</html>

HTML:
<!--body_minimal.html-->
<div id="demo">
	2+2=5
</div>

Code:
//minimal.js
function init(){document.getElementById('demo').innerHTML = '2+2=4';}

Bekomme als Ausgabe "2+2=5", der JS-Code wird also nicht oder nicht richtig ausgeführt... Fehler im Debugger gibt es dabei keine.
 
Natürlich wird der JS-Code der minimal.js nicht ausgeführt. Du rufst die Funktion init() ja nicht mehr auf.

Also entweder den "Wrapper" function init() { ... } entfernen, also in deiner minimal.js einfach nur Folgendes schreiben:
Code:
document.getElementById("demo").innerHTML = "2+2=4";
, oder in dem <script> tag wo die minimal.js eingebunden wird noch init() aufrufen:
Code:
    <!DOCTYPE html>
    <html>
    <head>
    	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    	<script>
    		document.addEventListener("DOMContentLoaded", function(e) {
    			Array.from(document.querySelectorAll(".include")).forEach(item => {
    			fetch(item.getAttribute("data-include")).then(response => {
    				return response.text();
    			}).then(html => {
    				item.innerHTML = html;
     
    				// load minimal.js
    				var js = document.createElement("script");
    				js.src = "minimal.js";
    				document.body.appendChild(js);
                                init(); // <---- Hier der Call
    			});
    		});
    	});
    	</script>
    </head>
    <body>
    	<div class="include" data-include="body_minimal.html"></div>
    </body>
    </html>
 
Jetzt funktioniert's nicht, weil init() aufgerufen wird, bevor die JS-Datei geladen & ausgeführt wurde.

Schmeiß' doch das init() aus deiner JS-Datei raus?
 
Super, danke, das war wohl bei meinen bisherigen Versuchen das Problem! Habe das jetzt auf die eigentliche Seite angewendet: https://serlo-graphs.github.io/JIT/Graphs/testgraph.html Dort muss ich ja mehrere JS-Dateien einbinden, das habe ich einfach hintereinander gemacht:

HTML:
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

	<!-- CSS Dateien -->
	<link type="text/css" href="../css/base.css" rel="stylesheet" />
	<link type="text/css" href="../css/Spacetree.css" rel="stylesheet" />

	<!-- JS-Dateien, Code von https://www.computerbase.de/forum/threads/einbinden-von-html-fuehrt-zu-js-fehler.1647096/#post-19622254 -->
    <script>
        document.addEventListener("DOMContentLoaded", function(e) {
            Array.from(document.querySelectorAll(".include")).forEach(item => {
            fetch(item.getAttribute("data-include")).then(response => {
                return response.text();
            }).then(html => {
                item.innerHTML = html;
 
                var js1 = document.createElement("script");
                js1.src = "../Graphs/testgraph.json";
                document.body.appendChild(js1);

                var js2 = document.createElement("script");
                js2.src = "../includes/jit.js";
                document.body.appendChild(js2);

                var js3 = document.createElement("script");
                js3.src = "../includes/graph.js";
                document.body.appendChild(js3);                            
            });
        });
    });
    </script>
</head>
<body>
	<!-- Body-Datei, Code von https://www.computerbase.de/forum/threads/einbinden-von-html-fuehrt-zu-js-fehler.1647096/#post-19622254 -->
    <div class="include" data-include="../includes/body.html"></div>
</body>
</html>

Manchmal klappt auch alles wunderbar, aber manchmal meldet der JS-Debugger

Code:
ReferenceError: $jit is not defined

Das hieße, so weit ich es überblicke, dass die JS-Datei ../includes/jit.js, die mit der Variable js2 geladen wird, nicht rechtzeitig geladen werden konnte. Was kann man denn da machen?
 
Pack doch die jit.js und die graph.js in eine Datei - wäre jetzt das einfachste was mir einfallen würde.

Alternativ musst du sicherstellen, dass die JS-Datei "jit.js" vor der "graph.js" geladen und ausgeführt wurde.
 
Ok, habe nun die beiden anderen Dateien wie vorher eingebunden und die graph.js mit der von dir vorgeschlagenen Konstruktion:

HTML:
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

	<!-- CSS Dateien -->
	<link type="text/css" href="../css/base.css" rel="stylesheet" />
	<link type="text/css" href="../css/Spacetree.css" rel="stylesheet" />

	<!-- JIT Library File -->
	<script language="javascript" type="text/javascript" src="../includes/jit.js"></script>

	<!-- Graph tree data -->
	<script language="javascript" type="text/javascript" src="../Graphs/testgraph.json"></script>

	<!-- JS-Dateien, Code von https://www.computerbase.de/forum/threads/einbinden-von-html-fuehrt-zu-js-fehler.1647096/#post-19622254 -->
    <script>
        document.addEventListener("DOMContentLoaded", function(e) {
            Array.from(document.querySelectorAll(".include")).forEach(item => {
            fetch(item.getAttribute("data-include")).then(response => {
                return response.text();
            }).then(html => {
                item.innerHTML = html;

                var js = document.createElement("script");
                js.src = "../includes/graph.js";
                document.body.appendChild(js);                            
            });
        });
    });
    </script>
</head>
<body>
	<!-- Body-Datei, Code von https://www.computerbase.de/forum/threads/einbinden-von-html-fuehrt-zu-js-fehler.1647096/#post-19622254 -->
    <div class="include" data-include="../includes/body.html"></div>
</body>
</html>

Das scheint zu funktionieren: https://serlo-graphs.github.io/JIT/Graphs/testgraph.html

Somit wäre das Problem gelöst, vielen Dank für die Unterstützung!
 
Zurück
Oben