Erstelle eine formatierte „Thank you notification“ in Realtime Formularen

Neulich erhielt ich den Wunsch eines Kunden, die Erfolgsnachricht nach einer Formularübermittlung eines Microsoft 365 Customer Insights – Journeys Formulars (realtime) mit diversen Formatierungen darzustellen. Außerdem sollte ein Button ergänzt werden, der die Seite neu lädt. „Kann ja so schwer nicht sein“ dachte ich mir. Mit den Standardevents kam ich jedoch nicht besonders weit, weshalb ein wenig um die Ecke denken angesagt war. Deshalb möchte ich meinen Ansatz gern teilen, denn ich glaube, dass diese Anforderung gar nicht so selten ist.

Was ist das Ziel?

Microsoft erlaubt uns im Standard nach der Formularübermittlung zwei Dinge: die Darstellung eines reinen Textes (ohne Formatierung, ohne Umbrüche) oder die Weiterleitung auf eine externe Webseite. Was nun aber, wenn beides meinen Anforderungen nicht genügt? Das Ziel war die Darstellung der folgenden Nachricht.

Um einen flexiblen Ansatz umzusetzen, wollte ich weiterhin das Feld „Thank you notification“ nutzen, nur eben mit HTML Inhalt und nicht reinem Textinhalt. Diese Spalte lässt standardmäßig nur 500 Zeichen zu. Wenn man mehr benötigt, sollte man ggf. überlegen, die Nachricht im Code direkt zu implementieren.

<h3 style="text-align: center">Thank you, we have received your request.</h3>
<p style="text-align: center">Here you get back to the
  <strong>
    <a
      style="color: #56bd00; text-decoration: none"
      href="https://www.dummypage.com"
      >Shop
    </a>
  </strong>.
</p>
<p style="color: #56db00; text-align: center">
  <strong>
    <a id="reload" style="color: #56db00; text-decoration: none" href="#"
      >Reload page</a>
    </strong>
</p>

Leider ist das Formular im Standard nicht in der Lage, hier HTML zu interpretieren. Ohne Anpassungen würde der HTML Code einfach als Text dargestellt werden. Außerdem existiert das Element mit der Nachricht im Browser erst, wenn nach der Formularübermittlung die Seite neu gerendert wird. Den Textinhalt als HTML darzustellen, ist relativ simpel. Der zweite Punkt ließ mich jedoch etwas länger brüten.

Different approaches

Mein erster Ansatz war, das von Microsoft zur Verfügung gestellte Event „d365mkt-afterformsubmit“ zu nutzen, um anschließend das neue Element zu suchen und dann den Inhalt als HTML zu interpretieren. Blöderweise ist zum Zeitpunkt dieses Events die Seite noch nicht angepasst, wodurch die Suche nach der Nachricht im DOM erfolglos blieb.

Der zweite Ansatz war, einen Timeout hinzuzufügen. Das geht natürlich, ist aber auch sehr unschön, da man irgendwie raten muss, wie lange wohl der Renderingprozess der Erfolgsnachricht braucht und im besten Fall dann ein „Flackern“ erzeugt, wenn der Inhalt erst als Text angezeigt wird und dann wenige Millisekunden später als HTML neu gerendert wird.

Der dritte Ansatz war vielversprechender: Auf modernen Seiten – vor allem mit endlos Scrolling – kommt häufig die „Mutation Observer“ Methode zum Einsatz, um beispielsweise zu erkennen, wann sich ein Element ganz oder teilweise im sichtbaren Screenbereich befindet. Dies ist viel performanter, als z.B. einen Eventlistener auf Scrolling zu implementieren. Das Schöne: auch wir können uns diese Methode zu Nutze machen und beobachten damit das Marketingformular und reagieren auf Änderungen an diesem Element.

Was passiert nach der Formsubmission?

Das <form>-Element wird komplett verschwinden und durch ein <div> mit der Erfolgsnachricht ersetzt.

Vorher:

Nachher:

Das <form>-Element wird von einem <main>-Element umgeben. Dieses übergeordnete Element werden wir mit dem Mutation Observer beobachten.

Der Mutation Observer im Einsatz

Ich werde nicht näher auf die Methode an sich eingehen, wer dazu mehr Infos haben möchte, kann sich bei den mdn web docs einen guten Überblick verschaffen. Im Prinzip wartet er auf die Veränderung von verschiedensten Eigenschaften eines DOM-Elements und gibt uns die Möglichkeit, mit einer Callbackfunction darauf zu reagieren.

Der Ansatz ist also, das übergeordnete <main>-Element zu beobachten und bei einer Änderung zu prüfen, ob die Erfolgsnachricht vorhanden ist. Wenn sie gefunden wird, wird der Inhalt der Nachricht als HTML neu interpretiert.

Der Code enthält ein paar mehr Zeilen, ist aber eigentlich relativ „straight-forward“. Gehen wir ihn einmal Schritt für Schritt durch.

class RealtimeFormExtensions {
    constructor() {
        this.form = document.querySelector('form.marketingForm'); // Selects the first Customer Insights Form on the page
        this.formParentElement = this.form ? this.form.parentElement : null;

        if (!this.form || !this.formParentElement) {
            return; // do not run if form or parentElement is not found.
        }

        this.formObserver = null;
        this.events();
    }

    events() {
        this._observeBodyForContentChange();
    }

    _observeBodyForContentChange() {
        this.formObserver = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    const successMessage = this.formParentElement.querySelector('.formSubmittedFeedbackMessage');

                    if (successMessage) {
                        // Replace the encoded HTML entities with actual HTML
                        successMessage.innerHTML = successMessage.textContent;
                        this._cleanupObserver(); // Stop observing once the change is made
                    }
                }
            });
        });

        this.formObserver.observe(this.formParentElement, {
            childList: true,
            subtree: true
        });
    }

    _cleanupObserver() {
        if (this.formObserver) {
            this.formObserver.disconnect();
        }
    }
}

document.addEventListener('d365mkt-afterformload', function () {
    new RealtimeFormExtensions();
});

Grundsätzlich habe ich mir angewöhnt, objektorientiert zu schreiben, also definiere ich erst einmal meine Klasse „RealtimeFormExtensions“. Der constructor ist der Teil der Klasse, der immer ausgeführt wird, wenn die Klasse instanziert wird – in diesem Fall, wenn das Event d365mkt-afterformload ausgelöst wird (am Ende des Codeausschnitts). Die Methode events() dient für mich als zentrale Stelle, um alle notwendigen Beobachtungen und Aktionen zu starten, die durch die Klasse ausgeführt werden. Durch die objektorientierte Struktur bleibt der Code modular, übersichtlich und einfach erweiterbar.

Im Constructor (der Initialisierungsfunktion) suche ich das Formular via document.querySelector(‚form.marketingForm‘) und suche anschließend noch das übergeordnete Parent Element (also das <main>-Element) via this.form?.parentElement.

Wenn eines der beiden nicht gefunden wird, breche ich in Zeile 7 den Code ab.

Anschließend erstelle ich noch die Variable formObserver im Kontext der Klasse und führe die Methode events() aus, die alle meine Events beinhaltet, die ich durchführen möchte. In diesem Fall ist das nur _observeBodyForContentChange(). Man kann das alles auch viel kürzer schreiben, aber ich finde es so besser lesbar.

Der eigentlich spannende Teil: Mutation Observer im Einsatz

Schauen wir uns den Mutation Observer im Detail an. Zunächst definieren wir den Observer (ab Zeile 19). Wir erstellen eine neue Instanz von MutationObserver und definieren eine Callbackfunktion, die auf die mutations reagieren soll.

Bei jeder mutation prüfen wir erstmal, ob es sich um eine Änderung der childList handelt (mutation.type === ‚childList‘) und ob mind. ein neues Element hinzugefügt wurde (mutation.addedNodes.length > 0).

Ist das beides wahr, suchen wir nach dem Element, welches die Erfolgsnachricht beinhaltet. const successMessage = this.formParentElement.querySelector(‚.onFormSubmittedFeedbackMessage‘)

Können wir das Element finden, müssen wir den Inhalt anpassen. Das ist relativ simpel: wir nehmen den textContent aus dem Element und definieren diesen um als „innerHTML“: successMessage.innerHTML = successMessage.textContent

Anschließend beenden wir den Mutation Observer noch, weil wir ihn nicht länger benötigen.

Nun müssen wir den von uns definierten Mutation Observer noch starten und ihm sagen, was er eigentlich überwachen soll. Das tun wir in Zeile 33 mit der Methode „observe“, die wir auf unseren this.formObserver anwenden. Als ersten Parameter übergeben wir das zu überwachende Element, unser Parent Element des Formulars und als zweiten Parameter können wir noch einige Optionen mitgeben, damit auch die childList-Änderungen mit überwacht werden.

Bonus: Seite per Klick neuladen

In meinem beispielhaften HTML habe ich einen Link eingebaut, mit dem die Seite neu geladen werden soll. Wir können unser Script noch ein wenig erweitern, um diese Funktionalität zu ergänzen. Dazu verwenden wir simples Standard Javascript, allerdings müssen wir darauf achten, dass wir den Event Listener, der auf den Klick reagieren soll, erst registrieren dürfen, wenn der Link auch auf der Webseite ist, also wenn zuvor unsere Logik bereits gelaufen ist.

Wir können eine kleine Methode zur Klasse hinzufügen:

_setupReloadLink() {
        const reloadLink = document.getElementById('reload');
        if (reloadLink) {
            reloadLink.addEventListener('click', (event) => {
                event.preventDefault();
                location.reload();
            });
        }
    }

Diese Methode können wir nun innerhalb der _observeBodyForContentChange() Methode am Schluss aufrufen.

_observeBodyForContentChange() {
        this.formObserver = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    const successMessage = this.formParentElement.querySelector('.formSubmittedFeedbackMessage');

                    if (successMessage) {
                        successMessage.innerHTML = successMessage.textContent;

                        // Now that the success message is present, set up the reload link
                        this._setupReloadLink();

                        this._cleanupObserver();
                    }
                }
            });
        });

Fazit

Durch den Einsatz des Mutation Observer können wir die Einschränkungen des Standardformulars von Microsoft 365 Customer Insights – Journeys elegant überwinden. Dieser Ansatz ermöglicht es uns, eine flexible, formatierte Erfolgsmeldung anzuzeigen, die eine reibungslosere Benutzererfahrung gewährleistet. Mit ein wenig cleverem Denken können wir die Standardfunktionalitäten erweitern und mehr Anpassungsoptionen anbieten, ohne die Leistung zu beeinträchtigen.


Bleibe informiert

Verpasse keinen neuen Beitrag und abonniere einfach den Newsletter.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert