Rolf B: DOMParser und Progressive Enhancement

Beitrag lesen

problematische Seite

So, und jetzt das JavaScript. Sind 90 Zeilen geworden, eine Menge davon für die Logik der Defaultbuttons. Diese Demo-Version kann nur ein Form auf der Seite. Ohne jegliche JS Libs. Natürlich nur zusammengeklopfter Code. Wenn ich den als "dein Werk" auf deiner Homepage wiederfinde, gibt's übrigens Ärger. Ich werde das bei Interesse ins Self Wiki stellen.

Beim Start wird das Form gesucht und geprüft, ob darin updatePanel Elemente sind, die auch eine ID haben. Wenn nicht, passiert nix. Geschachtelte Updatepanels sind nicht sinnvoll und dürften zu Kuddelmuddel führen, das wird nicht geprüft sondern per GIGO abgehandelt. Ansonsten wird das Form in ein FormData serialisiert und verschickt.

receiveAjaxForm überlagert den Inhalt der Updatepanels mit dem neuen Inhalt.

serializeForm macht die Arbeit. Gemäß Clean Code habe ich etliches in Funktionen gegliedert, dadurch sind Kommentare eigentlich unnötig.

Wenn Du debuggen willst: kein Breakpoint vor der Fokusbestimmung. Sonst ist der Fokus weg 😂

Script und HTML sind zu 99% voneinander unabhängig. Einziger gemeinsamer Nenner ist der Klassenname 'updatePanel' und der Name des data-submitBtn Attribut. DAS ist Entkoppelung.

// Demonstrationscode für die SelfHTML Community. 
// Autor: Rolf Borchmann (SelfHTML User Rolf B). Verwendung auf eigenen Seiten mit Nennung des Autors.
document.addEventListener("DOMContentLoaded", function() {
	"use strict";
	// ---- seitenspezifische Initialisierung. Würde den Rest normalerweise als Modul importieren
	registerAjaxForm(document.querySelector("form"));
		
	// --------------- Eine Lösung als Modul muss exakt diese Funktion exportieren. 
	function registerAjaxForm(form) {
		let panels = form.querySelectorAll(".updatePanel[id]");
		if (panels.length < 1) return;
	
		form.addEventListener("submit", e => sendAjaxForm(e, form, panels));
	}
	
	// -------------------- Code ab hier bleibt private für das Modul
	
	function sendAjaxForm(e, form, panels) {
		let data = serializeForm(form);

		let req = new XMLHttpRequest();
		req.open("POST", form.action);
		req.responseType = "document";
		req.onload = e => receiveAjaxForm(e, panels);
		req.send(data);
		e.preventDefault();
	}
	
	function receiveAjaxForm(loadedEvent, panels) {
		let xhr = loadedEvent.target;
		if (xhr.status != 200) {
			alert("Request failed, Status="+xhr.status);
			return;
		}
		for (let i=0; i<panels.length; i++) {
			let updatePanel = panels[i];
			let newPanel = xhr.response.getElementById(updatePanel.id);
			if (newPanel != null)
				updatePanel.innerHTML = newPanel.innerHTML;
		}
		return;
	}
	
	function serializeForm(form) {
		let data = new FormData(form);
		let submitter = locateSubmitButton(form);
		if (submitter) {
			data.append(submitter.name, submitter.value);
		}
		return data;
	}
	
	function locateSubmitButton(form) {
		let submitter = form.querySelector(":focus");
		if (isSubmitButton(submitter)) {
			return hasValidName(submitter) ? submitter : undefined;
		}
		if (isInputElement(submitter)) {
			let s = locateDefaultSubmitter(submitter);
			if (!s) return undefined;
			s = document.getElementById(s);
			return (s && hasValidName(s)) ? s : undefined;
		}
		return undefined;
	}
	
	function isSubmitButton(elem) {
		if (!(elem instanceof HTMLButtonElement || 
				elem instanceof HTMLInputElement))
			return false;
		return elem.type == "submit"; 
	}
	
	function isInputElement(elem) {
		if (elem instanceof HTMLTextAreaElement) return true;
		if (!(elem instanceof HTMLInputElement)) return false;
		return (elem.type == "text" || elem.type == "radio" || elem.type == "checkbox");
	}
	
	function locateDefaultSubmitter(elem) {
		if (elem.dataset.submitbtn) return elem.dataset.submitbtn;
		if (elem instanceof HTMLFormElement) return null;
		return locateDefaultSubmitter(elem.parentElement);
	}
	
	function hasValidName(elem) {
		return typeof elem.name == "string" && elem.name.trim() != "";
	}
});

Rolf

--
sumpsi - posui - clusi