Nummerierung bei dynamischen Formular
Jochbart
- formulare
- html
- javascript
Hallo, ich bin gerade dabei ein Formular zu erstellen, jedoch bekomme ich es bei der dynamischen Ergänzung nicht mit der fortlaufenden Nummerierung hin:
<html>
<head>
<title>Test</title>
<script type="text/javascript">
<!--
function clone_this(objButton)
{
if(objButton.parentNode)
{
tmpNode=objButton.parentNode.cloneNode(true);
objButton.form.appendChild(tmpNode);
for(j=0;j<objButton.form.lastChild.childNodes.length;++j)
{
if(objButton.form.lastChild.childNodes[j].type=='text')
{
objButton.form.lastChild.childNodes[j].value='';
break;
}
for(i=1; i<100; ++i)
var platz = "Arbeitsplatz " +i;
document.getElementById("arbeit")
.innerHTML = platz;
}
objButton.value="entfernen";
objButton.onclick=new Function('f1','this.form.removeChild(this.parentNode)');
}
}
//-->
</script>
</head>
<body>
<form>
<div>
<h2 id="arbeit">Arbeitsplatz 1</h2>
<label for="uberschrift">Überschrift:</label>
<input size="100"><br>
<br>
<label for="inhalt">Text:</label>
<input size="100"><br>
<br>
<label for="bild">Bild:</label>
<input type="file"><br>
<br>
<label for="video">Video:</label>
<input type="file"><br>
<br>
<label for="schluss">Schlusstext:</label>
<input size="100"><br>
<br>
<input type="button"value="noch eins"onclick="clone_this(this)">
</div>
</form>
</body>
</html>
Auch versuche ich den entfernen Button neben den "Neuen Arbeitsplatz"-Button zu bekommen und dass dieser auch jeweils nur für das letzte erschaffenden Arbeitsplatz gilt bzw. nur dieser gelöscht werden kann.
Wäre super wenn mir jemand unter die Arme greifen könnte. Schon mal ein großes Danke!
Gruß, Jochbart
Hallo Jochbart,
ich versuche mal zu beschreiben, was ich davon verstanden habe, was passieren soll.
Einiges davon willst Du sicherlich nicht, anderes ist sehr mühsam umgesetzt und einiges komplett falsch...
type='text'
hinzu. Vorteil: dann kannst Du sie mit querySelector bzw. querySelectorAll finden.Das letzte Problem - Umgang mit den geklonten Buttons - stelle ich noch zurück. Soll der "Noch eins" Button in jedem div stehen? Oder nur im letzten div?
Es wäre ggf. sinnvoller, den "Noch eins" Button aus dem div herauszunehmen und unter das form zu setzen. Den "entfernen" Button sollte man auf jedes div setzen. Es braucht dann nur etwas Logik (bzw einen div:not(:only-of-type) Selektor im CSS), um zu verhindern, dass man den letzten div block löscht.
Das Thema "Nummerieren der DIV-Überschriften" kann man übrigens besser mit CSS Nummerierungen lösen, dann musst Du kein JavaScript dafür bauen.
Wenn Du dein form nachher an PHP posten willst, musst Du die name-Attribute der input-Elemente übrigens mit einem [] ergänzen, damit PHP daraus ein Array macht.
Rolf
Hi Rolf,
mal wieder vielen Dank dass du dir so viel Mühe gibst beim beantworten meiner (dummen) Fragen. Ich habe jetzt mal den Code soweit angepasst wie ich es von dir verstanden habe (also 1-3):
<html>
<head>
<title>Test</title>
<script type="text/javascript">
<!--
function clone_this(objButton)
{
if(objButton.parentNode)
{
tmpNode=objButton.parentNode.cloneNode(true);
objButton.form.appendChild(tmpNode);
for(j=0;j<objButton.form.lastChild.childNodes.length;++j)
{
if(objButton.form.lastChild.childNodes[j].type=='text')
{
objButton.form.lastChild.childNodes[j].value='';
break;
}
for(i=1; i<100; ++i)
var platz = "Arbeitsplatz " +i;
document.getElementById("arbeit")
.innerHTML = platz;
}
objButton.value="entfernen";
objButton.onclick=new Function('f1','this.form.removeChild(this.parentNode)');
}
}
//-->
</script>
</head>
<body>
<form>
<div>
<h2>Arbeitsplatz</h2>
<label for="uberschrift">Überschrift:</label>
<input type="text" name="headline" size="100"><br>
<br>
<label for="inhalt">Text:</label>
<input type="text" name="inhalt" size="100"><br>
<br>
<label for="bild">Bild:</label>
<input type="file" name="pic"><br>
<br>
<label for="video">Video:</label>
<input type="file" name="vid"><br>
<br>
<label for="schluss">Schlusstext:</label>
<input type="text" name="schluss" size="100"><br>
<br>
</div>
</form>
<input type="button"value="noch eins"onclick="clone_this(this)">
</body>
</html>
Ich möchte im Grunde nur diesen Arbeitsplatz, beim drücken des Buttons ohne Inhalt kopieren, da ich nicht weiss wie viel Arbeitsplätze es im Endeffekt werden können. Dabei sollen die Arbeitsplätze fortlaufend nummeriert werden von 1 an. Das habe ich mit der Schleife versucht nur bekomme es überhaupt nicht hin. Wenn der Benutzer ausversehen einmal zu viel auf "noch eins" geklickt hat, kann er dieses wieder durch einen 2. Button löschen, sprich rückgängig machen.
Hallo Jochbart,
ich versuche mal zu beschreiben, was ich davon verstanden habe, was passieren soll.
- Du hast ein Form, mit einer div-Gruppe, bestehend aus einem h2 Heading, ein paar Input-Feldern und einem "Noch eins" Button.
- Klick auf den "Noch eins" Button klont die div-Gruppe, zu der er gehört und hängt den Klon als als letztes Element ins Form.
Richtig!
- Mit relativ mühsamer DOM-Turnerei löschst Du die Eingabefelder im Klon
Wie geht es einfacher?
- dann kommt eine Schleife, die der Variablen "platz" die Werte von "Arbeitsplatz 1" bis "Arbeitsplatz 99" zuweist
Soll eig. sich der länge der erschaffenen Arbeitsplätze anpassen, weiss aber nicht wie ich es realisieren kann, deswegen einfach mal als Wert <100 genommen.
- den letzten Wert davon (also Arbeitsplatz 99) weist Du an das Element mit der id "Arbeit" zu. Davon gibt's durch den Klon mittlerweile 2, die Zuweisung erreicht das erste davon
Das war nicht gewollt! Ich wollte damit bezwecken dass das Hochzählen bei dieser id statt findet.
- Füge den input-Feldern, die type=text haben sollen, explizit
type='text'
hinzu. Vorteil: dann kannst Du sie mit querySelector bzw. querySelectorAll finden.- Gib den input-Feldern ein name Attribut, sonst sind die Labels sinnlos (und sie werden beim POST nicht an den Server übertragen)
- Gib der h2 keine id. Nach dem Clone hast Du die Id mehrfach, das ist also sinnlos.
-->Geändert
Das letzte Problem - Umgang mit den geklonten Buttons - stelle ich noch zurück. Soll der "Noch eins" Button in jedem div stehen? Oder nur im letzten div?
Immer ganz unten beim letzten, hinzugefügten div.
Es wäre ggf. sinnvoller, den "Noch eins" Button aus dem div herauszunehmen und unter das form zu setzen. Den "entfernen" Button sollte man auf jedes div setzen. Es braucht dann nur etwas Logik (bzw einen div:not(:only-of-type) Selektor im CSS), um zu verhindern, dass man den letzten div block löscht.
Hab den Button mal unter form verschoben. Ich dachte ursprünglich eig. dass ich den entfernen Button auch nur ganz zum Schluss nach jedem neu hinzugefügten div haben möchte, falls man einmal zu viel "noch eins" geklickt hat. Aber was meinst du von der Usebility her was sinnvoller ist?
Das Thema "Nummerieren der DIV-Überschriften" kann man übrigens besser mit CSS Nummerierungen lösen, dann musst Du kein JavaScript dafür bauen.
Das schaue ich mir mal an.
So habe jetzt die Nummerierung wie in dem Tutorium in CSS gemacht, jedoch zählt es nicht nach oben sonder bleibt bei jedem neuen Arbeitsplatz immer bei 1 😟
body {
font: normal 15px Arial;
}
label {
display: block;
margin: 5px 0;
}
h2 {
counter-increment: posMarkup;
position: relative;
}
h2::after {
content: counter(posMarkup);
padding-left: 1em;
width: 1em;
left: 0;
}
Hallo Jochbart,
gib dem body noch ein counter-reset: posMarkup 0 - scheint ohne nicht zu gehen.
Und für einen Cheater-Blick hier mein Spaß-Fiddle: https://jsfiddle.net/wffou3x6/
Rolf
Vielen DANK für diese Hilfestellung! Macht einiges sinnvoller für mich jetzt!
hi,
Wäre super wenn mir jemand unter die Arme greifen könnte. Schon mal ein großes Danke!
Am Einfachsten macht sich sowas über ein Template was Loops kann. Die Idee dahinter: Anstatt im DOM rumzufummeln um irgendwo ChildNodes anzuhängen, wird an ein vorher definiertes Array ein Objekt angehängt, wobei die Objekt-ID einfach als fortlaufende Nummer geführt wird.
Und nach dem Anfügen weiterer Objekte wird diese Datenstruktur nur noch über ein Template gerendert, fertig. MfG
Hallo pl,
da hast Du natürlich prinzipiell recht - es ist besser, wenn man nicht auf DOM Ebene fummelt, sondern direkt im Datenmodell. Das setzt dann ein automatisiertes Model-View Mapping voraus (z.B. react oder knockout, oder größere Brocken wie Angular). Aber damit wollte ich Jochbart jetzt nicht kommen, ich hatte Sorgen um seine Herzklappen 😉
Da hier direkt ein Form aufgebaut wird, habe ich mir auch überlegt, dass die Daten direkt zum Server gepostet werden (daher auch mein Hinweis mit dem [] im Namen).
Rolf
Hi Rolf,
Das setzt dann ein automatisiertes Model-View Mapping voraus
Was man braucht ist ganz einfach nur eine Datenstruktur die über ein Template gerendert werden kann, i.d.R. ist das ein Array der Bauart [{id:1},{id:2},{id:3}..]
was also zur TE passen muss. Die Objekte (Arrayelemente) können beliebige Eigenschaften haben, z.B. {name:value}
sofern Formfelder vorzubelegen sind und ggf. eine id bzw. fortlaufende Nummer.
Beispiel zur Demo
Hallo pl,
Am Einfachsten macht sich sowas über ein Template was Loops kann. Die Idee dahinter: Anstatt im DOM rumzufummeln um irgendwo ChildNodes anzuhängen, wird an ein vorher definiertes Array ein Objekt angehängt, wobei die Objekt-ID einfach als fortlaufende Nummer geführt wird.
Und nach dem Anfügen weiterer Objekte wird diese Datenstruktur nur noch über ein Template gerendert, fertig. MfG
Hört sich logisch an, aber die Rolf schon gesagt hat, bin ich mit dieser Aussage komplett überfordert. Wo bekomme ich denn solche Templates her?
Ich habe Rolfs Code einmal übernommen und mir gestern Nacht noch durchgeschaut, ich denke ich kann alles nachvollziehen und habe es verstanden.
Jetzt ist mir noch in den Sinn gekommen, dass der Input des Feldes "Überschrift" sich beim eintippen gleichzeitig hinter den Arbeitsplatz einfügt. Von dem Tutorial hier, habe ich folgenden Code gefunden:
<h1>Beispiel: keyup Event</h1>
<main>
<p>Sobald Sie eine Taste loslassen, wird über keyup ein Ereignis ausgelöst.</p>
<form
name="Test" action="">
<input type="text" size="30" name="Eingabe" onkeyup="this.form.Ausgabe.value = this.value">
<br>
<input type="text" readonly size="30" name="Ausgabe">
<br>
<input type="reset"> </form>
</main>
Ich dachte ich kann es für meinen Fall so übernehmen, jedoch kommt es bei mir nur bei Arbeitsplatz 1 und sobald ich die weiteren hinzufüge funktioniert es nicht mehr...aber warum? Mein Code:
<form>
<div>
<h2><span>Arbeitsplatz </span> - <input type="text" readonly size="30" name="Ausgabe"><button type="button" name="remove">Entfernen</button></h2>
<label for="uberschrift"><span>Überschrift:</span><input type="text" name="uberschrift" size="80" onkeyup="this.form.Ausgabe.value = this.value">
</label>
<label for="inhalt"><span>Text:</span><input type="text" name="inhalt" size="80"></label>
<label for="bild"><span>Bild:</span><input name="bild" type="file"></label>
<label for="video"><span>Video:</span><input name="video" type="file"></label>
<label for="schluss"><span>Schlusstext:</span><input name="schluss" type="text" size="80"></label>
</div>
</form>
<button id="newPlace" type="button">Neuer Arbeitsplatz</button>
<button id="enter" type="submit">Daten abschicken</button>
Hallo Jochbart,
es gibt da ein paar Fallstricke, die sich um deine Waden wickeln.
Aber eins hast Du aus meiner Vorlage nicht verstanden: "unobtrusive javascript". Heißt: Man verwendet heute eigentlich keine ondingsbums Attribute mehr - sie passen nicht zum Konzept der Aufgabentrennung HTML = Struktur, CSS = Layout, JavaScript = Verhalten. Statt dessen schreibt man im Script einen DOM-ready Handler: document.addEventListener("DOMContentLoaded", function() { ... });
und verkuppelt alle Event-Handler in dieser Funktion.
Wenn man eine Gruppe von Elementen hat, auf die man mit einem Eventhandler gleichartig reagieren will, dann registriert man den Eventhandler nicht auf jedem einzelnen Element. Statt dessen nutzt man die "bubble" Eigenschaft von JavaScript Events und registriert den Listener auf einem gemeinsamen Container dieser Elemente - bei Dir das form. Für den "Entfernen" Handler habe ich das in meinem Beispiel bereits gemacht, hier nochmal gezeigt:
form.addEventListener("click", removeSection);
function removeSection(evt) {
if (evt.target.name == "remove") form.removeChild(evt.target.parentNode.parentNode);
}
Dein Event für Änderungen auf dem Überschriften-Feld solltest Du ähnlich registrieren.
ABER die Frage vorweg wäre - warum machst Du das? Warum machst Du das input Element im h2 nicht einfach readwrite und verzichtest auf das Überschrift-Element?
Bei Dir konkret funktioniert nicht, dass this.form.Ausgabe
nicht existiert, es ist this.form.elements.Ausgabe
. Aber das ist nicht nur ein Element, sondern die Menge aller Elemente mit name="Ausgabe". Du müsstest also mindestens mal auf this.form.elements.Ausgabe[0]
zugreifen. Damit löst Du aber nicht das Problem, Änderungen des zweiten "uberschrift" input auch in das zweite "Ausgabe" Element zu übertragen. Um das zu machen, musst Du analog zur remove Funktion zuerst vom Element, auf dem das Ereignis ausgelöst wurde, auf den Container wechseln (also 2x .parentNode) und kannst darauf dann querySelector("input[name=Ausgabe]") aufrufen, um das gewünschte Element zu finden. Damit bekommst Du ein eindeutiges Ergebnis.
Ich würde dann aber auch nicht ein input-Feld in die Überschrift legen, sondern den span überschreiben wo "Arbeitsplatz" drinsteht. Es sei denn, das Überschriftfeld ist leer, dann setze "Arbeitsplatz" als Standardtext ein. Ich hab dazu mal wieder gespielt - wenn Du davon was übernehmen willst, beachte bitte dass ich aus dem div eine section gemacht habe, was Änderungen im CSS und JS zur Folge hat. Die Standardaufgabe, die Teilsektion zu finden, in der ein Element liegt, habe ich in eine Funktion verschoben.
Man könnte noch argumentieren, dass statt <section> eine <ol> Liste angebrachter wäre, weil semantisch passender. Wie man dann das Markup korrekt aufbaut, um auch Accessibility-konform zu sein, müsste jemand anderes beisteuern.
Für eine perfekte Lösung müsstest Du das Ganze noch so gestalten, dass es auch ohne JavaScript funktioniert. In diesem Fall müssten die Buttons das form posten und der Server baut es entsprechend neu auf. Das ist etwas Arbeit, aber für eine Zugänglichkeit der Seite nötig. Den Zusatz type='button'
bei den Aktionsbuttons lässt Du dann weg und rufst in den Eventhandler-Funktionen als letztes evt.preventDefault()
auf, um keinen Submit auszulösen. Ist JavaScript aus, führen die Buttons einfach einen Submit aus und du kannst am Server reagieren.
Rolf
Hi Rolf, danke für die ausführliche Antwort.
Ich würde dann aber auch nicht ein input-Feld in die Überschrift legen, sondern den span überschreiben wo "Arbeitsplatz" drinsteht. Es sei denn, das Überschriftfeld ist leer, dann setze "Arbeitsplatz" als Standardtext ein. Ich hab dazu mal wieder gespielt - wenn Du davon was übernehmen willst, beachte bitte dass ich aus dem div eine section gemacht habe, was Änderungen im CSS und JS zur Folge hat. Die Standardaufgabe, die Teilsektion zu finden, in der ein Element liegt, habe ich in eine Funktion verschoben.
Aber sowas möchte ich ja nicht, sondern lediglich dass der Input der Überschrift sich mit einem - getrennt hinter dem Arbeitsplatz so und so live einfügt, wie ich es in meinem Code versucht habe.
Für eine perfekte Lösung müsstest Du das Ganze noch so gestalten, dass es auch ohne JavaScript funktioniert. In diesem Fall müssten die Buttons das form posten und der Server baut es entsprechend neu auf. Das ist etwas Arbeit, aber für eine Zugänglichkeit der Seite nötig. Den Zusatz type='button' bei den Aktionsbuttons lässt Du dann weg und rufst in den Eventhandler-Funktionen als letztes evt.preventDefault() auf, um keinen Submit auszulösen. Ist JavaScript aus, führen die Buttons einfach einen Submit aus und du kannst am Server reagieren.
Damit kann ich leider nichts anfangen, ich verstehe es nicht.
Gruß, Jochbart
Hallo Jochbart,
Aber sowas möchte ich ja nicht (...)
Na dann modifiziere es halt 😉. span-inhalt = "Arbeitsplatz - " + uberschrift-text. Ich habe den Counter extra VOR den "Arbeitsplatz" gesetzt, damit er in diesem Zusammenbau nicht stört.
Für eine perfekte Lösung ... (a11y blabla) Damit kann ich leider nichts anfangen, ich verstehe es nicht.
Dann lass es einfach beiseite. Die Themen Accessibility und Progressive Enhancement sind komplex. Wenn die Lösung für Dich trägt, ist es doch gut. Perfektion muss nicht auf Anhieb kommen.
Rolf
Hi Rolf, ja das scheint etwas komplizierter zu sein, wenn ich wieder auf after umstelle und die Überschrift dann aber dahinter haben möchte. Ich habe es jetzt mal auf before gelassen aber aus einem mir nicht ersichtlichen Grund kommt dann ab 2. Arbeitsplatz dahinter automatisch null und erst wenn ich anfange was in das Überschriftfeld zu tippen wird null mit meinem Text ersetzt. Habe auch schon versucht mit " " als Platzhalter zu arbeiten, aber dann erscheint gar nichts mehr.
Gruß Jochbart
Hallo Jochbart,
Gibt's eine URL zum live anschauen?
Rolf
Servus,
ja hier ist mein fiddle dazu. Das komische ist jedoch, dass es auf meinem PC funktioniert und jetzt wo ich es bei fiddle rein hab funktioniert weder die Überschriften Funktion noch neuer Arbeitsplatz. Vielleicht kannst du mir sagen warum?
Wenn das aber klappen sollte, dann siehst du hoffentlich mein Problem, dass immer null ab Arbeitsplatz 2 auftaucht. Des weiteren verstehe ich nicht, warum er mir bei meinem neuen Dropdown-Feld in der Funktion dazu nicht mein br einfügt sondern alles untereinander klatscht.
Wenn ich doch statt 1. Arbeitsplatz - Überschrift, Arbeitsplatz 1 -Überschrift haben möchte versuche ich das besser in CSS oder in der HTML Datei zu ändern?
Grüße,
Jochbart
Hallo Jochbart,
das ist jetzt eine Menge, die ich schreiben muss 😀
#Eventregistrierung Bei Fiddle muss man angeben, wann das JavaScript läuft. Dazu klickt man auf das Zahnrad neben dem Wort "JAVASCRIPT" und wählt den LOAD TYPE. Standard ist "onLoad", d.h. jsFiddle wartet für Dich auf das load-Event des Body und führt dein JS dann aus. Das load-Event fliegt aber erst nach DOMContentReady, deshalb wird dein Eventhandler nie ausgeführt.
In meinem Fiddle hatte ich das auf "no wrap - in head" gestellt, weil ich Dir den vollständigen Code zeigen wollte, den Du brauchst, um es im eigenen Script einzusetzen. Wenn ich das bei Dir umstelle, wird das Script ausgeführt bevor die Seite aufgebaut wird und "Neuer Arbeitsplatz" funktioniert wieder.
#Umgang mit einem leeren hdrValue in der updateHeader-Funktion.
Bei mir gab's das "Arbeitsplatz" vor dem Zusatztext nicht, deshalb konnte ich mit hdrValue || "Arbeitsplatz"
agieren. Lies Dir mal durch, was ich hier geschrieben habe, insbesondere über truthy und falsy, und am Anfang von „Wahrheitstabellen und Kurzschlussreaktionen“ die Info über das zurückgelieferte Ergebnis von ||.
Wenn Du "Arbeitsplatz - " + hdrValue || "Arbeitsplatz"
schreibst, wird zunächst die Stringverkettung durchgeführt, weil + Vorrang vor || hat (siehe auch hier). Ist hdrValue == null, wandelt JavaScript den null Wert in den String "null" um und verkettet das. Das Ergebnis ist ein nicht-leerer String, das ist ein truthy Wert und darum ist das das Ergebnis des || Operators. Ich hätte das in meinem Fiddle nicht machen sollen, das ist Expertenwissen zu Javascript. Mach es so:
if (hdrValue != null)
header.innerText = "Arbeitsplatz - " + hdrValue;
else
header.innerText = "Arbeitsplatz ";
oder mit ?: Operator so:
header.innerText = hdrValue ? ("Arbeitsplatz - " + hdrValue) : "Arbeitsplatz ";
#Zum Thema „1. Arbeitsplatz - Überschrift“ vs „Arbeitsplatz 1 - Überschrift“ Ich würde das weiterhin mit CSS zählen, weil Du sonst nach jeder Löschung von Hand neu nummerieren musst. Damit das mit CSS funktioniert, nimmst Du den Text "Arbeitsplatz" aus dem Span heraus und lässt den span per Default erstmal leer. Dann musst Du kaum was ändern.
Also im HTML:
<h2>Arbeitsplatz <span></span><button type="button" name="remove">Entfernen</button></h2>
Im CSS:
h2 span:before { content: counter(plaetze); counter-increment: plaetze 1; display:inline; }
Und im JavaScript:
header.innerText = hdrValue ? (" - " + hdrValue) : "";
Jaaa. Und nun zum Horror-Teil.
#Die Bilderliste.
container
nicht mehr auf ein einzelnes div zeigt, sondern ein Array der container enthält. Das JavaScript stürzt dann mit "container.appendChild is not function" ab. Weiter unten mehr dazu.<label><span></span><input type="file" ...></label>
aus. Da das CSS das label auf display:block
setzt, brauchst Du dann auch kein <br> mehr.D.h. hinter die Registrierung des input-Events für das form setzt Du die Registrierung und die Filterung auf den richtigen Elementtyp:
form.addEventListener("change", function(evt) {
if (evt.target.name == "top10")
updateBilderUpload(sectionFor(evt.target), evt.target.value);
});
und weiter unten, aber nicht zu weit :), dann die Handler-Funktion. Der Rahmen hat bereits die Section und die Anzahl ermittelt. Hier musst Du nun noch die Liste finden und dich entscheiden, wie Du vorgehen willst. Soll es insgesamt so viele Upload-Elemente geben, wie als "anzahl" übergeben wird? Oder sollen so viele Elemente hinzugefügt werden? Davon hängt ab, was die Funktion tun soll, deswegen schreibe ich hier nur einen Rahmen hin.
function updateBilderUpload(section, anzahl) {
var liste = section.querySelector("ul.bilder");
// Beispiel: Ein neues li mit passendem Inhalt erzeugen
var newItem = document.createElement("li");
newItem.innerHtml = "<label><span>Bild:</span><input type='file' name='..'></label>"
}
Bevor wir uns über den Inhalt von updateBilderUpload unterhalten - wie soll's denn sein?
ABER ABER ABER: Du gerätst jetzt in Zuordnungsprobleme. Jedes Bild-Uploadelement hat name="bild". Du hast am Server keine Ahnung mehr, welches Bild zu welchem Arbeitsplatz gehört hat. Vor dem Submit musst Du die name-Elemente aller Bilder patchen, so dass die Arbeitsplatznummer im Bild enthalten ist. Das darfst Du auch erst beim submit-Element des form tun, weil es ja vorher sein kann, dass munter Arbeitsplätze hinzugefügt oder entfernt wurden. D.h. du brauchst einen weiteren Eventhandler:
form.addEventListener("submit", function() {
// Hier die Namen der input-Elemente systematisch neu nummerieren
});
Dieser Handler sollte dann nicht nur die File-Elemente nummerieren, sondern alle. Um über das "wie" zu reden, wäre erstmal die Frage: Welche Sprache verwendest Du am Server?
Rolf
Guten Abend Rolf,
habe seit gestern bis gerade mit deinem Beitrag noch gespielt bzw. selber versucht es umzugestalten. Manches verstehe ich noch nicht so ganz. Mach es so:
if (hdrValue != null) header.innerText = "Arbeitsplatz - " + hdrValue; else header.innerText = "Arbeitsplatz ";
oder mit ?: Operator so:
header.innerText = hdrValue ? ("Arbeitsplatz - " + hdrValue) : "Arbeitsplatz ";
Hab ich verstanden und auch umgesetzt. Danke!
Also im HTML:
<h2>Arbeitsplatz <span></span><button type="button" name="remove">Entfernen</button></h2>
Im CSS:
h2 span:before { content: counter(plaetze); counter-increment: plaetze 1; display:inline; }
Und im JavaScript:
header.innerText = hdrValue ? (" - " + hdrValue) : "";
Auch verstanden und angewendet, sogar mit if else. :P
#Die Bilderliste.
- kein onchange Attribut am Dropdown!.…
- die Eventhandler-Funktion setzt Du, so wie die anderen Handlerfunktionen auch, IN die Handlerfunktion für DOMContentLoaded hinein.
Hoffe ich habe es richtig umgesetzt.
- statt einem div für die Bilderupload-Liste (klingeling!) wäre eine Liste angemessener. Also ein ul als Container und für jedes Bild ein li. ul und li werden so gestyled dass sie keinen margin, kein padding und keinen list-style haben. Die li enthalten dann jeweils ein Upload-Feld. Dem ul gibst Du, um es identifizieren zu können, eine class (weil Du vermutlich die gleich Nummer noch mit Video-Uploads planst). Mein Vorschlag: class="bilder".
Richtig, genau so wollte ich bei den Videos auch vorgehen. Ich habe es mal versucht auf meine Art und Weise zu machen, jedoch verstehe ich nicht warum es nicht mit getElementByClassName klappt.
- das von Dir konstruierte HTML passt nicht zu dem vorhandenen Bild-Upload. Du hast das label Element mit darin befindlichem input type="file" aus meinem Fiddle übernommen. Was Du in deinen Container einfügst, ist einfach nur ein input-Element. Deswegen greift das CSS nicht. Das CSS geht von der Struktur
<label><span></span><input type="file" ...></label>
aus. Da das CSS das label aufdisplay:block
setzt, brauchst Du dann auch kein <br> mehr.
Habe ich das input-Element jetzt richtig eingefügt mit dem Typ file?
- du hast ein Dropdown "Anzahl der hochzuladenden Bilder". Dein Eventhandler fügt aber soviele input-Felder hinzu, wie man ausgewählt hat. D.h. wenn ich erst 2, dann 3 und dann 9 auswähle, habe ich am Ende 15 Felder. Ist das Dein Wunsch? Möchtest Du nicht vielleicht lieber einen "noch ein Bild" Button haben?
Nein du hast recht, ich möchte nur dass so viele Uploads erscheinen wie auch ausgewählt also nix mit addieren. Bei mir im Code klappt das soweit auch nur dass ich halt immer noch beim div hänge weil es mit meiner class bzw ul nicht hinhaut.
D.h. hinter die Registrierung des input-Events für das form setzt Du die Registrierung und die Filterung auf den richtigen Elementtyp:
form.addEventListener("change", function(evt) { if (evt.target.name == "top10") updateBilderUpload(sectionFor(evt.target), evt.target.value); });
und weiter unten, aber nicht zu weit :), dann die Handler-Funktion. Der Rahmen hat bereits die Section und die Anzahl ermittelt. Hier musst Du nun noch die Liste finden und dich entscheiden, wie Du vorgehen willst. Soll es insgesamt so viele Upload-Elemente geben, wie als "anzahl" übergeben wird? Oder sollen so viele Elemente hinzugefügt werden? Davon hängt ab, was die Funktion tun soll, deswegen schreibe ich hier nur einen Rahmen hin.
function updateBilderUpload(section, anzahl) { var liste = section.querySelector("ul.bilder"); // Beispiel: Ein neues li mit passendem Inhalt erzeugen var newItem = document.createElement("li"); newItem.innerHtml = "<label><span>Bild:</span><input type='file' name='..'></label>" }
Ich bekomme es mit dieser Art und Weise nicht hin, deswegen habe ich es mal als Kommentar hinterlegt. Vielleicht habe ich es auch nicht richtig verstanden und einfach an die falsche Position gesetzt?!
Bevor wir uns über den Inhalt von updateBilderUpload unterhalten - wie soll's denn sein?
ABER ABER ABER: Du gerätst jetzt in Zuordnungsprobleme. Jedes Bild-Uploadelement hat name="bild". Du hast am Server keine Ahnung mehr, welches Bild zu welchem Arbeitsplatz gehört hat. Vor dem Submit musst Du die name-Elemente aller Bilder patchen, so dass die Arbeitsplatznummer im Bild enthalten ist. Das darfst Du auch erst beim submit-Element des form tun, weil es ja vorher sein kann, dass munter Arbeitsplätze hinzugefügt oder entfernt wurden. D.h. du brauchst einen weiteren Eventhandler:
form.addEventListener("submit", function() { // Hier die Namen der input-Elemente systematisch neu nummerieren });
Funktioniert das nicht mit meinem gut gemeinten "bild" + i in der Schleife um die Nummerierung selbst zu gestalten? Ich suche gerade noch nach einem Weg ein System einzuführen nach dem Schema, der Dateiname des Uploads nimmt als Name die Dateiendung an. D.h. wenn ich z.B. ein .jpg hoch lade wird vor dem Namen der Datei JPG-blabla geschoben und abgespeichert. Ist so etwas möglich?
Dieser Handler sollte dann nicht nur die File-Elemente nummerieren, sondern alle. Um über das "wie" zu reden, wäre erstmal die Frage: Welche Sprache verwendest Du am Server?
Zum Thema Server muss ich mich noch schlau machen. Deswegen bin ich für jeden Tipp dankbar wenn es um die Sprache geht. Ist es auch möglich meine Uploads durch abschicken des Formulars in vorher festgelegte Ordner auf der Festplatte zu speichern?
Gruss,
Jochbart
P.S. Achja vor lauter losgeschreibe ja glatt meinen jetzigen Code vergessen: fiddle
Hallo Jochbart,
zuerst ein paar kleinere Antworten:
getElementByClassName funktioniert nicht, weil die Funktion getElementsByClassName heißt. Klassen-Zuordnungen sind nicht eindeutig, deshalb Elements. Du bekommst dann kein Element, sondern eine NodeList mit allen passenden Elementen zurück. Statt getElementsByClassName kannst Du auch querySelector oder querySelectorAll verwenden, das ist etwas allgemeiner. Meine Fiddles haben das immer eingesetzt.
Ich habe Dich belogen - es gibt keine innerHtml Eigenschaft. Sie heißt innerHTML. Deswegen hat das nicht funktioniert, was ich letztens geschrieben habe.
IDs funktionieren in deiner Aufgabenstellung nicht. Es hilft nicht, ein dropdown per id finden zu wollen, nach dem ersten clone hast Du die IDs mehrfach. Deswegen musst Du immer von einem Event-Target (ob nun click, input oder change) hoch zur Section - dafür habe ich Dir eine Funktion gemacht - und von da aus mit getElementsByClassName, querySelector oder querySelectorAll INNERHALB der section zum richtigen Zielelement. Ich habe Dir ein Muster gezeigt, an dem Du Dich orientieren kannst. Oben stehen die Event-Registrierer, diese haben eine KLEINE Inline-Funktion, die sicherstellt dass nur das Event von der richtigen Quelle verarbeitet wird, dann die Section ermittelt und falls nötig noch weitere Daten zum Element. Damit wird dann die Bearbeitungsfunktion aufgerufen.
Zum Beispiel diese existierende Registrierung hier, für das input-Event der Überschrift:
form.addEventListener("input", function (evt) {
if (evt.target.name == "uberschrift")
updateHeader(sectionFor(evt.target), evt.target.value);
});
Hast Du Dir die Doku zu addEventListener durchgelesen? Folgendes ist hier wichtig und sollte von Dir auch für das change-Event auf dem Dropdown beachtet werden!
target
, in der das Element steht auf dem das Event ausgelöst wurde. Wenn ich das change-Event für alle Dropdowns behandeln will, die für einen Arbeitsplatz die Anzahl der Bilder steuern, dann muss ich diesem select-Element Merkmale geben, die ich abfragen kann. Das kann eine Klasse sein (abfragen mit classList.contains("klassenname")), ein name-Attribut oder ich kann auch den tagName abfragen. Eine ID ist nicht geeignet. IDs müssen auf der Seite eindeutig sein. Das Beispiel oben hat das name-Attribut geprüft. Das kannst Du für das Dropdown auch tun (top10 ist allerdings kein guter Name, weil du ähnliches ja auch noch für die Videos tun willst)updateHeader(sectionFor(evt.target), evt.target.value)
<section>...</section>
eingeschlossen ist. Das Funktionsergebnis, also das section-Element innerhalb dessen das event ausgelöst wurde, sowie im Beispiel die value-Eigenschaft des Elements auf dem das Event ausgelöst wurde, werden als Parameter an updateHeader übergeben.#Und nun der traurige Teil:
Es betrübt mich, dich betrüben zu müssen: Ohne serverseitigen Code kommst Du meines Wissens nicht weiter. Du brauchst eine Komponente, die die angegebenen Dateien oder Videos entgegennimmt und irgendwo ablädt. Zumindest dann, wenn Du die Dateien über den POST eines HTTP form ablädst. Das kann ein PHP Script sein oder sonst ein programmiertes Artefakt, aber der multipart/form-data Body des POST-Request, den ein HTML-Form mit input type="file" ausspuckt, wird serverseitig nicht ohne dein Zutun verstanden.
Das HTTP Protokoll kennt auch noch andere Verben außer GET und POST - eins davon ist PUT. Wenn Dein Webserver so konfiguriert ist, per PUT Dateien anzunehmen, kannst Du auf diese Weise Dateien hochladen. Aber nicht mit dem Submit eines HTML-Formulars.
PHP ist sicherlich der einfachste Weg, um hier etwas auf die Beine zu bekommen. SelfHTML ist allerdings nicht sehr stark in PHP Tutorials. Wir haben aber ein kleines PHP Portal mit vielen Links.
Speichern der Bilder allein reicht aber nicht. Du brauchst ja auch eine Idee, wie die hochgeladenen Arbeitsplatzinformationen nachher einem Benutzer präsentiert werden können. Und wie Du sie auf deinem Server speichern willst. Eine Datenbank? Eine Verzeichnisstruktur mit einem Ordner pro AP und eine html-Datei, die die Bilder und Videos präsentiert?
Oh f*ck. Ich werde hier noch zum Prof, wenn ich mir den Umfang dieses geistigen Auswurfs betrachte…
Rolf
Hallo Rolf,
- getElementByClassName funktioniert nicht, weil die Funktion getElementsByClassName heißt.
Sorry mein Tippfehler hier. Ich habe natürlich im Code getElementsByClassName benutzt, nur hier eben vergessen. Die Frage war eher darauf bezogen warum es dennoch nicht funktioniert...
- Ich habe Dich belogen - es gibt keine innerHtml Eigenschaft. Sie heißt innerHTML. Deswegen hat das nicht funktioniert, was ich letztens geschrieben habe.
Ja das habe ich bemerkt gehabt, funktioniert leider immer noch nicht. 😟
- IDs funktionieren in deiner Aufgabenstellung nicht. Es hilft nicht, ein dropdown per id finden zu wollen, nach dem ersten clone hast Du die IDs mehrfach.…
Das habe ich schon bereits aus deinem Beitrag zuvor verstanden, deswegen habe ich mich ja versucht auf den name zu beziehen was jedoch nicht funktionierte, also bin ich wieder zurück zur container id um es lediglich dir zu zeigen (hatte Kommentar hinterlegt).
Zum Beispiel diese existierende Registrierung hier, für das input-Event der Überschrift:
form.addEventListener("input", function (evt) { if (evt.target.name == "uberschrift") updateHeader(sectionFor(evt.target), evt.target.value); });
Hast Du Dir die Doku zu addEventListener durchgelesen? Folgendes ist hier wichtig und sollte von Dir auch für das change-Event auf dem Dropdown beachtet werden!
Ja ich habe es mir durchgelesen und verstehe auch (denke ich) dieses input Event. Ich habe es versucht mit Hilfe deines addEventListener für anzBilder es zu realisieren, jedoch ohne Erfolg.
- Der Handler wird nicht auf dem Input-Element, sondern auf dem form registriert. Mit Absicht - ein input-Event wird zwar auf dem Input-Element ausgelöst, blubbert dann aber in der Elementhierarchie des Dokuments hoch, bis ganz nach oben zum body-Element (oder sogar bis zum html-Element? Weiß grad nicht). D.h. wenn ich den Eventhandler auf dem form registriere, bekomme ich alle input-Events mit, die irgendwo im form gefeuert werden, ohne mich explizit auf Elemente innerhalb des Forms registrieren zu müssen. Das spart Arbeit beim Registrieren und beim Klonen.
Das verstehe ich.
- Der Handler bekommt als Parameter ein Event-Objekt (-> Doku+Tutorials), und darin gibt's eine Eigenschaft
target
, in der das Element steht auf dem das Event ausgelöst wurde. Wenn ich das change-Event für alle Dropdowns behandeln will, die für einen Arbeitsplatz die Anzahl der Bilder steuern, dann muss ich diesem select-Element Merkmale geben, die ich abfragen kann. Das kann eine Klasse sein (abfragen mit classList.contains("klassenname")), ein name-Attribut oder ich kann auch den tagName abfragen. Eine ID ist nicht geeignet. IDs müssen auf der Seite eindeutig sein. Das Beispiel oben hat das name-Attribut geprüft. Das kannst Du für das Dropdown auch tun (top10 ist allerdings kein guter Name, weil du ähnliches ja auch noch für die Videos tun willst)
Ich werde auch nur noch mit anzBilder und anzVideos arbeiten.
- Wenn Du außer für die Bilder auch noch ein Dropdown für die Videos haben willst, brauchst Du keine neue Eventhandler-Registrierung. Du hast schon einen Eventhandler, und in dem baust Du eine zweite Prüfung für Event-Quellen ein. Für name="top10" (oder "anzBilder") wird die Liste der Bild-Uploads geändert, und für name="anzVideos" die Liste der Video-Uploads.
Ich bekomme ja nicht einmal das für die Bilder gerade hin, deswegen möchte ich erst mal Schritt für Schritt voran gehen. Wenn ich es hinbekomme für die Bilder wird es ja das selbe Schema F für die Videos sein.
Da steht:
updateHeader(sectionFor(evt.target), evt.target.value)
Mir scheint, dass Du noch nicht verstanden hast, was da passiert - du hast diesen Teil nämlich konsequent ignoriert. Hier wird zuerst die sectionFor Funktion aufgerufen und bekommt das Event-Target als Parameter. Die sectionFor-Funktion beginnt bei dem übergebenen DOM Element und sucht sich über die parentNode Kette den Weg nach oben, bis es ein Element mit tagName = 'section' findet. Dein HTML ist so gegliedert, dass jeder Arbeitsplatz in ein<section>...</section>
eingeschlossen ist. Das Funktionsergebnis, also das section-Element innerhalb dessen das event ausgelöst wurde, sowie im Beispiel die value-Eigenschaft des Elements auf dem das Event ausgelöst wurde, werden als Parameter an updateHeader übergeben.
Diese Beschreibung von dir hat auf jeden Fall nochmal richtig klick gemacht bei mir! Danke dafür. Was ich jedoch immer noch nicht so richtig denke zu verstehen ist die sectionFor Funktion. Für was brauche ich zum Beispiel .toLowerCase? Also ich kenn die Funktion davon aber mir leuchtet nicht ein warum ich das an dieser Stelle brauche.
- Dein Handler, der die Upload-Elemente erzeugt, sollte genauso aufgerufen werden. Deine addInputFields Funktion sollte also - so wie updateHeader - zwei Parameter erwarten: Die Section und die Anzahl. Und sie sollte nicht addInputFields heißen, ihr Name sollte darlegen, dass sie die Anzahl der Bilder-Uploads festlegt.
Damit spiele ich die ganze Zeit schon...klappt aber nicht so wie ich das möchte.
- Damit das Folgende funktioniert, braucht dein HTML noch eine Anpassung. Das Label mit dem Bild-Upload Element darin muss in einem li in dem vorbereiteten Bilder-ul stehen, sonst hast Du nachher eins zuviel. Und das div mit id="container" ist Kunst, die weg kann.
Diese 2 Punkte habe ich schon gar nicht mehr beachtet, sie waren einfach noch Überbleibsel vom Anfang.
- Dann hängst Du dem ul die benötigte Zahl von neuen li ein. Im CSS musst Du sicherstellen, dass der list-style von ul.bilder auf none steht, damit Du die kleinen Punkte davor nicht hast. Ggf. musst Du auch noch margin und padding von ul und li anpassen. Da, wo ich name='..' geschrieben habe, musst Du natürlich etwas Zeichenketten-Akrobatik betreiben, damit in den Hochkommas nachher sowas wie bild_7 steht. Tipp: 'bild_'+i funktioniert - JavaScript erkennt, dass da ein String mit einer Zahl verbunden wird und wandelt i in eine Zeichenkette um.
CSS habe ich ja schon geschrieben gehabt, hatte es nur in Kommentare gesetzt um die Punkte am Anfang zu sehen um sicher zu gehen das es klappt. Und mit "bild_"+1 hatte ich ja schon in meiner Schleife leicht abgeändert benutzt.
Es betrübt mich, dich betrüben zu müssen: Ohne serverseitigen Code kommst Du meines Wissens nicht weiter. Du brauchst eine Komponente, die die angegebenen Dateien oder Videos entgegennimmt und irgendwo ablädt. Zumindest dann, wenn Du die Dateien über den POST eines HTTP form ablädst. Das kann ein PHP Script sein oder sonst ein programmiertes Artefakt, aber der multipart/form-data Body des POST-Request, den ein HTML-Form mit input type="file" ausspuckt, wird serverseitig nicht ohne dein Zutun verstanden.
Ich hatte gehofft dabei irgendwie auf PHP bzw. Server zu verzichten oder sogar offline das ganze nur auf einem lokalen Rechner (Laufwerk für "Backup" ist vorhanden) abzuspeichern und zu betreiben.
PHP ist sicherlich der einfachste Weg, um hier etwas auf die Beine zu bekommen. SelfHTML ist allerdings nicht sehr stark in PHP Tutorials. Wir haben aber ein kleines PHP Portal mit vielen Links.
Schau ich mir auf jeden Fall die Tage an!
Speichern der Bilder allein reicht aber nicht. Du brauchst ja auch eine Idee, wie die hochgeladenen Arbeitsplatzinformationen nachher einem Benutzer präsentiert werden können. Und wie Du sie auf deinem Server speichern willst. Eine Datenbank? Eine Verzeichnisstruktur mit einem Ordner pro AP und eine html-Datei, die die Bilder und Videos präsentiert?
Ja eine weitere HTML-Datei/Seite zum präsentieren soll es auf jeden Fall sein! Wie gesagt ich dachte eher an eine Ordnerstruktur auf dem Laufwerk oder Rechner und von dort die "Uploads" zu speichern und abzurufen. Ich hatte auch mal irgendwo was von AJAX und File APi, hab aber keine Ahnung wie das geht.
Oh f*ck. Ich werde hier noch zum Prof, wenn ich mir den Umfang dieses geistigen Auswurfs betrachte…
Also Rolf, ich glaube wir müssen uns mal treffen. Ich schulde dir viel und bezahle gerne in Bier. 😉
Mein "neues" fiddle. Sorry es wird immer mehr mit Kommentaren durch meine Rumspielerei.
Gruß, Jochbart
Rolf
sumpsi - posui - clusi
Hallo pl,
ok, um im bekannten Bild zu bleibem: das wäre dann die Palette mit Dosenfisch statt des Angelkurses 😂
Funktioniert der http PUT, mit dem Du uploadest, ohne serverseitigen Code? Was muss man am Server tun, um hier den Einfall von Ungeziefer jeder Art zu verhindern? Und hast Du auch eine Jochbart-kompatible Einführung in den Gebrauch deiner TE? Ich hab mir jetzt nicht die Zeit genommen, das js auf "Sofortverständlichkeit für Experten" zu parsen, aber Jochbart ist Novize, der Experte werden will, und für Novizen braucht man etwas mehr Verständlichkeit.
Rolf
hi,
Funktioniert der http PUT, mit dem Du uploadest, ohne serverseitigen Code? Was muss man am Server tun, um hier den Einfall von Ungeziefer jeder Art zu verhindern?
Die Request Methode ist doch völlig egal. Entscheidend ist, dem Request einen Enctype mitzugeben, damit der serverseitige Prozess weiß was zu tun ist. Ganz bestimmt nicht werde ich mit multipart/form-data meinen Server vergewaltigen sondern für jede hochzuladende Datei einen eigenen HTTP-Message Body verwenden, also je Datei einen Request. Oder noch besser ein Chunked Upload
Und hast Du auch eine Jochbart-kompatible Einführung in den Gebrauch deiner TE? Ich hab mir jetzt nicht die Zeit genommen, das js auf "Sofortverständlichkeit für Experten" zu parsen, aber Jochbart ist Novize, der Experte werden will, und für Novizen braucht man etwas mehr Verständlichkeit.
Den Aufbau der Templates habe ich in einem anderen Artikel beschrieben. Der ist hier im Thread weiter oben auch verlinkt. Man muss sich nur ein bischen Zeit dafür nehmen und {{mustache}} gibt es schließlich auch.
MfG
Hallo pl,
ich kann leider damit (noch) recht wenig anfangen. Was bedeutet denn TE? Ist damit gemeint ich soll mir die Seite als Quelltext anschauen? Ich finde ansonsten nirgends einen Code dazu…
Gruß,
Jochbart
Hallo Jochbart,
Was bedeutet denn TE?
template engine.
Bis demnächst
Matthias
Hallo Jochbart,
sorry wenn ich derzeit weniger präsent bin; ich bin mit der Familie auf Herbstferientour.
Das .toLowerCase in sectionFor() war ein Fall von "ich hab keine Ahnung, worauf ich mich verlassen kann". Sind tagName-Inhalte immer in Großschrift? Oder in Kleinschrift? Hängt es davon ab was der Seitenautor geschrieben hat? Ist es browserabhängig? Keine Ahnung, keine Lust zum recherchieren gehabt - aber ein .toLowerCase() bringt mich auf die sichere Seite. Damit ist der Wert definitiv in Kleinschrift. Wenn JavaScript einen kompakteren Operator für "Vergleiche Strings ohne Beachtung von Groß-/Kleinschreibung" besäße, dann hätte ich den genommen.
Wenn ich in deinem Fiddle nicht jede Spielwiese vom echten Zeug unterscheiden konnte, dann bitte ich um Entschuldigung. Ich hab dann vielleicht zu schnell entschieden, was nun was ist. Dein neues Fiddle guck ich mir jetzt noch an.
Rolf
@@Rolf B
Das .toLowerCase in sectionFor() war ein Fall von "ich hab keine Ahnung, worauf ich mich verlassen kann". Sind tagName-Inhalte immer in Großschrift? Oder in Kleinschrift? Hängt es davon ab was der Seitenautor geschrieben hat? Ist es browserabhängig?
“In XML (and XML-based languages such as XHTML), tagName
preserves case. On HTML elements in DOM trees flagged as HTML documents, tagName
returns the element name in the uppercase form.” [MDN]
Wenn JavaScript einen kompakteren Operator für "Vergleiche Strings ohne Beachtung von Groß-/Kleinschreibung" besäße, dann hätte ich den genommen.
/^CaMeL$/i.test('cAmEl'); // true
/^ying$/i.test('yang'); // false
Was nicht heißen soll, dass man das so machen sollte.
LLAP 🖖
Hallo Gunnar,
och, eine RegEx am Morgen lenkt ab von allen anderen Sorgen.
Rolf
Hallo Jochbart,
habe jetzt einen kurzen Blick auf dein Fiddle werfen können - Version 1270, wow :)
Auf jeden Fall sind 3 Fehler drin, die alles sabotieren:
if (i=0, i<anzBilder, i++)
1: Der Befehl für Schleifen heißt for, nicht if. 2: das Komma muss ein Semikolon sein
Das bösartige ist ja, dass deine Formulierung inhaltlich Murks, aber syntaktisch richtig ist und deshalb keine Fehlermeldung in der Konsole erscheint. Der Komma-Operator dient dazu, mehrere Ausdrücke nacheinander auszuwerten. Nur das Ergebnis des letzten wird weiterverwendet. D.h. deine Formulierung macht dies:
Der dritte Fehler ist, dass createElement auf document aufgerufen werden muss. Das Objekt in der Variablen liste repräsentiert ein UL Element und kennt die Methode nicht.
So, ich geh jetzt schlafen, morgen muss Berlin weiter erforscht werden. Viel Glück!
Rolf
@@Rolf B
morgen muss Berlin weiter erforscht werden.
In welcher Gegend treibste dich denn rum?
Ich bin am Potsdamer Platz, aber mit dem Fahrrad auch schnell zwei, drei Kilomenter woanders. Mittag? Kaffee?
LLAP 🖖
Hi Rolf,
Hallo Jochbart, habe jetzt einen kurzen Blick auf dein Fiddle werfen können - Version 1270, wow :)
Wo sieht man die Version? 🙈
1: Der Befehl für Schleifen heißt for, nicht if. 2: das Komma muss ein Semikolon sein
Oh man ich bin doch ein Idiot. 😂 War anscheinend doch zu spät gestern beim rumspielen.…
Der dritte Fehler ist, dass createElement auf document aufgerufen werden muss. Das Objekt in der Variablen liste repräsentiert ein UL Element und kennt die Methode nicht.
Danke, das wusste ich nicht. Wurde gefixt.
So, ich geh jetzt schlafen, morgen muss Berlin weiter erforscht werden. Viel Glück!
Viel Spaß!
Gruß,
Jochen
Hallo Jochen,
die Version siehst Du in der URL, bei jedem Update geht der Zähler eins hoch.
FileAPI habe ich selbst noch nicht benutzt, es dient aber dazu, Dinge auf der Maschine des Anwenders zu speichern, nicht auf dem Server. Entspricht das deinen Zielen?
Wenn Du die Anzahl der Bilder/Videos erhöhen oder verringern willst, ohne zu löschen, dann musst Du den Eventhandler etwas anders programmieren. Du musst zuerst prüfen, ob Du die Anzahl der Upload-Elemente erhöhen oder verringern musst (oder ob sie gleichbleibt, dann tust Du gar nichts). Wenn erhöhen, dann füge solange Elemente hinzu bis die Anzahl stimmt. Wenn verringern, dann entferne solange das letzte Element bis die Anzahl stimmt. Aber was machst Du, wenn Du die Bilder 3-5 behalten willst? Dann funktioniert das nicht mehr, dann sind die Anzahl-Dropdowns ein Irrweg. Deswegen sollte man, bevor man zu programmieren beginnt, eine Vision haben, wie das Gesamtsystem ausieht, oder zumindest ein minimal lebensfähiges Produkt (minimal viable product, MVP) im Auge haben. Und sich über alle Operationen im Klaren sein, die man da ausführen will. Und sich ständig fragen: Gibt mein UI her, was ich tun will?
Das Thema der Speicherung taucht dabei dann auch immer wieder auf. Die Namensvergabe für Ressourcen ist nur ein Teil. Ein KLEINER. Du bist auf dem Weg zu einem CRUD-System - nein Scherz, das steht für "Create, Read, Update, Delete", also die klassischen Operationen eines Systems zur Datenpflege. Aus Sicht eines Programmierers immer der gleiche Dreck...
Bisher haben wir nur über das C nachgedacht. Du kannst was anlegen, und irgendwann speichern. Du möchtest am nächsten Tag aber den Browser aufmachen und die gespeicherten Dinge wieder sehen. Du möchtest, dass Änderungen geändert bleiben und Löschungen gelöscht. Sowas setzt identifizierende Merkmale voraus. Identifizierend heißt: Es bleibt unveränderlich erhalten. Wenn Du einen Arbeitsplatz neu anlegst, braucht eine eine eindeutige ID. Wenn ein Bild hinzukommt, braucht es eine eindeutige ID. Ein Video auch. Wenn Du einen Arbeitsplatz löschst, wird seine ID nicht wiederverwendet, und die von ihm abhängigen Ressourcen (Bilder, Videos) müssen ebenfalls gelöscht werden. Wenn ein Bild/ein Video eine eindeutige ID hat, kannst du das Bild korrekt aus der Liste herauslöschen. Sonst nicht, bzw. löschst eventuell irgendwas, aber nicht das, was der User will.
Wenn Du ein serverloses System denkst, das mit File API arbeitet, machst Du das alles lokal im JavaScript und auf deiner Maschine, und hast das Problem konkurrierender Zugriffe nicht. Wenn Du mit einem Webserver arbeitest, dann übernimmt normalerweise der Server die Ablage und Organisation der Daten und Dateien. Und zwar eigenständig, auf Grund eines in PHP, Perl, Ruby, C#, Java oder sonstwie geschriebenen Programms (oder auch mit JavaScript, wenn Du node.js als Server nimmst, das ist aber deswegen nicht leichter). Vom Client aus die Ablage auf dem Server zu steuern, ihn also sozusagen nur als ferne Festplatte zu nutzen, mag mit HTTP Befehlen wie PUT und DELETE machbar sein, das muss aber entsprechend eingerichtet sein. Und vor allem kommt dann die Frage auf: Was ist, wenn zwei Leute die Anwendung gleichzeitig verwenden? PL hat das Ölkännchen gezückt und ist davongeflutscht, als ich diese Details seines Vorschlags herauslocken wollte. Aus gutem Grund. Da stecken nämlich viele Tretminen.
Tja. Worauf will ich eigentlich hinaus? Ich möchte Dich dazu zwingen, den "kein serverseitiger Code" Ansatz sehr gut zu durchdenken. Diese Entscheidung hat gravierende Konsequenzen. Und ob dich durch ein Projekt, das dahin läuft, per Forenberatung hindurchbegleiten MÖCHTE, das weiß ich nicht recht. Der Aufwand wird jetzt schon sehr groß. Einige Teile der Lernkurve musst Du selbst erklimmen. Bei Detailproblemen kann man Dir helfen. Aber nicht bei jedem einzelnen Schritt.
Rolf
Guten Abend Rolf,
die Version siehst Du in der URL, bei jedem Update geht der Zähler eins hoch.
Danke!
FileAPI habe ich selbst noch nicht benutzt, es dient aber dazu, Dinge auf der Maschine des Anwenders zu speichern, nicht auf dem Server. Entspricht das deinen Zielen?
Wenn du mit Maschine Computer/Festplatte/Laufwerk meinst, dann ja!
Wenn Du die Anzahl der Bilder/Videos erhöhen oder verringern willst, ohne zu löschen, dann musst Du den Eventhandler etwas anders programmieren. Du musst zuerst prüfen, ob Du die Anzahl der Upload-Elemente erhöhen oder verringern musst (oder ob sie gleichbleibt, dann tust Du gar nichts). Wenn erhöhen, dann füge solange Elemente hinzu bis die Anzahl stimmt. Wenn verringern, dann entferne solange das letzte Element bis die Anzahl stimmt. Aber was machst Du, wenn Du die Bilder 3-5 behalten willst? Dann funktioniert das nicht mehr, dann sind die Anzahl-Dropdowns ein Irrweg. Deswegen sollte man, bevor man zu programmieren beginnt, eine Vision haben, wie das Gesamtsystem ausieht, oder zumindest ein minimal lebensfähiges Produkt (minimal viable product, MVP) im Auge haben. Und sich über alle Operationen im Klaren sein, die man da ausführen will. Und sich ständig fragen: Gibt mein UI her, was ich tun will?
Ich möchte den Fall mit 3-5 Bildern einfach mal ignorieren um so auf meine Vorstellung zu kommen. Das sind dann (denke ich) eher Kleinigkeiten bzw. die Feinheiten die es (wenn überhaupt) zum Schluss hin ausgebessert werden können.
Bisher haben wir nur über das C nachgedacht. Du kannst was anlegen, und irgendwann speichern. Du möchtest am nächsten Tag aber den Browser aufmachen und die gespeicherten Dinge wieder sehen. Du möchtest, dass Änderungen geändert bleiben und Löschungen gelöscht. Sowas setzt identifizierende Merkmale voraus. Identifizierend heißt: Es bleibt unveränderlich erhalten. Wenn Du einen Arbeitsplatz neu anlegst, braucht eine eine eindeutige ID. Wenn ein Bild hinzukommt, braucht es eine eindeutige ID. Ein Video auch. Wenn Du einen Arbeitsplatz löschst, wird seine ID nicht wiederverwendet, und die von ihm abhängigen Ressourcen (Bilder, Videos) müssen ebenfalls gelöscht werden. Wenn ein Bild/ein Video eine eindeutige ID hat, kannst du das Bild korrekt aus der Liste herauslöschen. Sonst nicht, bzw. löschst eventuell irgendwas, aber nicht das, was der User will.
Wenn ich nach dem Schema vorgehe ID = Nummer aus Arbeitsplatz (z.B. Arbeitsplatz 1) + Nummer aus Bild (z.B. Bild Nr. 5) => ID = 1-5 müsste ich doch eine eindeutige ID damit bezwecken können oder? Ich versuche die ganze Zeit schon irgendwie den Value von meinem counter-increment in JS auszulesen. Bekomme es aber nicht hin...aber müsste doch mit getComputedStyle machbar sein oder nicht?
Wenn Du ein serverloses System denkst, das mit File API arbeitet, machst Du das alles lokal im JavaScript und auf deiner Maschine, und hast das Problem konkurrierender Zugriffe nicht. Wenn Du mit einem Webserver arbeitest, dann übernimmt normalerweise der Server die Ablage und Organisation der Daten und Dateien. Und zwar eigenständig, auf Grund eines in PHP, Perl, Ruby, C#, Java oder sonstwie geschriebenen Programms (oder auch mit JavaScript, wenn Du node.js als Server nimmst, das ist aber deswegen nicht leichter). Vom Client aus die Ablage auf dem Server zu steuern, ihn also sozusagen nur als ferne Festplatte zu nutzen, mag mit HTTP Befehlen wie PUT und DELETE machbar sein, das muss aber entsprechend eingerichtet sein. Und vor allem kommt dann die Frage auf: Was ist, wenn zwei Leute die Anwendung gleichzeitig verwenden? PL hat das Ölkännchen gezückt und ist davongeflutscht, als ich diese Details seines Vorschlags herauslocken wollte. Aus gutem Grund. Da stecken nämlich viele Tretminen.
Ich weiß nicht ob es zu Probleme kommen sollte wenn die HTML Datei hier auf einem Laufwerk liegen würde und mehrere Benutzer gleichzeitig sie benutzen. Im Endeffekt sollen ja durch die Formular-Maske Ordner erstellt werden können und die Dateien in den passenden gespeichert werden. Geht es denn, dass die Dateien automatisch ein Namenskürzel vorangestellt bekommen von JS? Das z.B. wenn es eine .jpg Datei ist der Name automatisch zu JPG-Bild5-1.jpg wird?
Tja. Worauf will ich eigentlich hinaus? Ich möchte Dich dazu zwingen, den "kein serverseitiger Code" Ansatz sehr gut zu durchdenken. Diese Entscheidung hat gravierende Konsequenzen. Und ob dich durch ein Projekt, das dahin läuft, per Forenberatung hindurchbegleiten MÖCHTE, das weiß ich nicht recht. Der Aufwand wird jetzt schon sehr groß. Einige Teile der Lernkurve musst Du selbst erklimmen. Bei Detailproblemen kann man Dir helfen. Aber nicht bei jedem einzelnen Schritt.
Ich durchdenke es nochmal die Tage und weiß natürlich, dass ich selbst das Meiste selber schaffen muss. Ich sitze ja auch so oft es geht am Rechner und verfolge die Strategie Try and Error. Auf jeden Fall bin ich euch und dir ganz besonders extrem dankbar für die Mithilfe. Ich hab dadurch schon super viele Fortschritte gemacht und verstehe immer mehr davon.
Gruß, Jochbart
@@Rolf B
Für eine perfekte Lösung müsstest Du das Ganze noch so gestalten, dass es auch ohne JavaScript funktioniert. In diesem Fall müssten die Buttons das form posten und der Server baut es entsprechend neu auf. Das ist etwas Arbeit, aber für eine Zugänglichkeit der Seite nötig.
Nei-en!
Es ist meist wünschenswert, dass eine Anwendung auch ohne JavaScript funktioniert. Mit Zugänglichkeit (Barrierefreiheit, accessibility, a11y) hat das aber nichts zu tun. Es ist vielmehr so, dass man mit JavaScript die Zugänglichkeit einer Anwendung verbessern kann.
LLAP 🖖