Eine Suchfunktion läuft nicht mehr


Status
Dieses Thema wurde gelöst! Zur Lösung gehen…

Webhufi

Erfahrenes Mitglied
Hallo,

ich hatte auf einer alten Seite - die leider verschwunden ist - eine Suche drin, die ich jetzt auf eine andere Page übernommen habe; nur diesen HTML-Teil hatte ich gerettet.
Leider funktioniert diese Suche nicht, wahrscheinlich fehlt etwas (ein Script?)

Ich erinnere mich, dass es recht einfach und ebenso kurz war, es funktionierte nur auf der Seite, in die es eingebaut war; mehr ist nicht notwendig.

Natürlich gibt es im Web solche Suchfunktionen, aber die sind mir alle zu aufgebauscht, und verstehen kann ich sie sowieso nicht.

Hat jemand eine Idee - und auch Lust dazu - diese Suche (etwas weiter unten) wieder zum Laufen zu bringen? Das würde mich sehr freuen!

Viele Grüße
Norbert
 

Sempervivum

Erfahrenes Mitglied
Hallo Norbert und willkommen zurück!
Um mich nicht durch die Ergebnisse von Google durchkämpfen zu müssen, habe ich schnell einen Algorithmus geschrieben, der die Textknoten des Dokumentes durchsucht und die Fundstellen in ein span-Element einbettet, so dass man sie mit CSS hervor heben kann.
Code:
        function search(node, needle) {
            node.childNodes.forEach(node => {
                switch (node.nodeType) {
                    // Handelt es sich um einen Elementknoten?
                    case 1:
                        // Suche fortsetzen
                        search(node, needle);
                        break;
                    // Handelt es sich um einen Textknoten?
                    case 3:
                        // Den Text heraus ziehen
                        const txt = node.textContent;
                        // Um den gefundenen Text hervor zu heben, betten wir ihn ein ein
                        // span-Element ein, das wir dann geeignet mit CSS gestalten koennen.
                        // In einem Textknoten wird jedoch kein HTML interpretiert.
                        // Daher erzeugen wir ein neues span-Element und tragen dort den
                        // geaenderten Text ein.
                        let newEle = document.createElement('span');
                        newEle.innerHTML = txt.replace(needle, '<span class="found">' + needle + '</span>')
                        node.replaceWith(newEle);
                        break;
                }
            });
        }

        // Die folgende Funktion wird durch das Suchformular aufgerufen
        // und startet die Suche
        function suchen(needle) {
            search(document.querySelector('body'), needle);
            return false;
        }
Was noch zu tun ist:
Das Durchgehen der Suchergebnisse mit Enter programmieren. Könnte ich mir so vorstellen, dass man den spans mit den Fundstellen eine nummerierte ID zuweist und diese dann als Anker verwendet, um dort hin zu springen.
 
Zuletzt bearbeitet:

Sempervivum

Erfahrenes Mitglied
Auch der nächste Schritt ist getan: Durchgehen der Fundstellen mit Enter:
Code:
    <script>
        function search(node, needle) {
            node.childNodes.forEach(node => {
                switch (node.nodeType) {
                    // Handelt es sich um einen Elementknoten?
                    case 1:
                        // Suche fortsetzen
                        search(node, needle);
                        break;
                    // Handelt es sich um einen Textknoten?
                    case 3:
                        // Den Text heraus ziehen
                        const txt = node.textContent.trim();
                        // console.log(txt)
                        if (txt != '') {
                            // Um den gefundenen Text hervor zu heben, betten wir ihn ein
                            // span-Element ein, das wir dann geeignet mit CSS gestalten koennen.
                            // In einem Textknoten wird jedoch kein HTML interpretiert.
                            // Daher erzeugen wir ein neues span-Element und tragen dort den
                            // geaenderten Text ein.
                            let newEle = document.createElement('span');
                            newEle.innerHTML = txt.replace(needle, '<span class="found">' + needle + '</span>')
                            node.replaceWith(newEle);
                            break;
                        }
                }
            });
        }
        let searchDone = false,
            foundElems,
            idxSearch = 0;
        // Die folgende Funktion wird durch das Suchformular aufgerufen
        // und startet die Suche
        function suchen(needle) {
            // event.preventDefault();
            // Wurde die Suche noch nicht gestartet?
            if (!searchDone) {
                // Suche starten
                search(document.querySelector('body'), needle);
                // Gefundene Element bereit stellen
                foundElems = document.querySelectorAll('.found');
                // Erstes gefundenes Element hervor heben
                foundElems[idxSearch].classList.add('highlight');
                foundElems[idxSearch].scrollIntoView();
                searchDone = true;
            } else {
                // Hevorhebung des alten gefundenen Elementes löschen
                foundElems[idxSearch].classList.remove('highlight');
                // Naechstes gefundenes Element hervor heben
                idxSearch++;
                foundElems[idxSearch].classList.add('highlight');
                foundElems[idxSearch].scrollIntoView();
            }
            return false;
        }
    </script>
Beste Grüße - Ulrich
 

Sempervivum

Erfahrenes Mitglied
PS: Wie ich jetzt erst sehe, funktioniert die alte Suche bei mir einwandfrei (Opera). Allerdings ist das Skript von Anno Tobak, da wird noch NS4 abgefragt :LOL:
 

Webhufi

Erfahrenes Mitglied
Hallo Ulrich,

danke für dein Zurück-Willkommen! :)
Wie du weißt, gibt es immer etwas Ungewöhnliches, wenn ich auftauche... ;-)

Dein Script scheint mir, soweit ich es verstehe (und das wird immer besser, vor allem durch deine Auskommentierungen), recht toll zu sein.
Jedoch funktioniert es nicht so, wie ich es verstehe: Hervorhebung gibt es keine, weitersuchen mit Enter geht nicht, bei einem nicht gefunden Wort gibt es keine Meldung wie im alten Script (z.B. Norbert), die Suche an sich ist case sensitiv: Einstein geht, aber nicht einstein. Schau bitte hier!

Was mir in deinem Script auffällt: Es gibt case 1 und case 3, aber kein case 2.

Dann bitte noch eine Erklärung: was ist ein Element- bzw. Textknoten? Nie gehört...

Herzliche Grüße
Norbert
 

Sempervivum

Erfahrenes Mitglied
Da habe ich etwas wichtiges vergessen: Das Skript setzt nur zwei Klassen, damit die Fundstellen sichtbar werden, muss man noch ein CSS definieren, z. B.:
Code:
.highlight {
    background-color: lightblue !important;
}

Was mir in deinem Script auffällt: Es gibt case 1 und case 3, aber kein case 2.
Das hängt mit den Typen von Knoten zusammen, siehe hier:
2, 4, 5, 6 sind deprecated, d. h. veraltet. Wir brauchen nur Elementknoten und Textknoten, also 1 und 3. Ein Knoten ist ein Element im Baum des DOM wo alle Elemente des HTML-Dokumentes entsprechend den Eltern-/Kind-Beziehungen definiert sind. Was im HTML z. B. ein div mit Text darin ist, ist im DOM etwas komplizierter, nämlich das div als Elementknoten und darin ein Textknoten mit dem Text darin. Bei unserer Suche ist das hilfreich, denn wir wollen ja nur Fundstellen im sichtbaren Text und nicht in Kommentaren, Attributen oder einem Skript.
 

Sempervivum

Erfahrenes Mitglied
bei einem nicht gefunden Wort gibt es keine Meldung wie im alten Script (z.B. Norbert), die Suche an sich ist case sensitiv: Einstein geht, aber nicht einstein.
Da ist anscheinend noch ein wenig Arbeit zu tun. Erst mal sehen, dass das Hervorheben und das Springen von einer Fundstelle zur nächsten funktioniert.
 

Sempervivum

Erfahrenes Mitglied
Jetzt nicht mehr case-sensitiv:
Code:
    <script>
        function search(node, needle) {
            const regex = new RegExp('(' + needle + ')', 'i');
            node.childNodes.forEach(node => {
                switch (node.nodeType) {
                    // Handelt es sich um einen Elementknoten?
                    case 1:
                        // Suche fortsetzen
                        search(node, needle);
                        break;
                    // Handelt es sich um einen Textknoten?
                    case 3:
                        // Den Text heraus ziehen
                        const txt = node.textContent.trim();
                        // console.log(txt)
                        if (txt != '') {
                            // Um den gefundenen Text hervor zu heben, betten wir ihn ein
                            // span-Element ein, das wir dann geeignet mit CSS gestalten koennen.
                            // In einem Textknoten wird jedoch kein HTML interpretiert.
                            // Daher erzeugen wir ein neues span-Element und tragen dort den
                            // geaenderten Text ein.
                            let newEle = document.createElement('span');
                            newEle.innerHTML = txt.replace(regex, '<span class="found">$1</span>')
                            node.replaceWith(newEle);
                            break;
                        }
                }
            });
        }
        let searchDone = false,
            foundElems,
            idxSearch = 0;
        // Die folgende Funktion wird durch das Suchformular aufgerufen
        // und startet die Suche
        function suchen(needle) {
            // event.preventDefault();
            // Wurde die Suche noch nicht gestartet?
            if (!searchDone) {
                // Suche starten
                search(document.querySelector('body'), needle);
                // Gefundene Element bereit stellen
                foundElems = document.querySelectorAll('.found');
                // Erstes gefundenes Element hervor heben
                foundElems[idxSearch].classList.add('highlight');
                foundElems[idxSearch].scrollIntoView();
                searchDone = true;
            } else {
                // Hevorhebung des alten gefundenen Elementes löschen
                foundElems[idxSearch].classList.remove('highlight');
                // Naechstes gefundenes Element hervor heben
                idxSearch++;
                foundElems[idxSearch].classList.add('highlight');
                foundElems[idxSearch].scrollIntoView();
            }
            return false;
        }
    </script>
 

Webhufi

Erfahrenes Mitglied
Aha, 'node' ist mir schon mal über den Weg gelaufen. Danke für die Erklärung!

Seltsam: Ich habe deinen neuen Code und auch den Style eingefügt, und es klappte prima in der Vorschau, auch mit Enter zum Weitersuchen. Dann habe ich im Suchfeld die Farbe geändert und alles hochgeladen; jetzt funktioniert die Suche nicht mehr... Was habe ich verbeutelt??? :-( An der Farbänderung liegt es sicher nicht.

P.S.: kein alert, sondern gar nichts! Das war vorher, als das Script noch funktionierte...

Jetzt mach bitte mal Pause, ich schaue morgen Abend wieder vorbei.
 

Sempervivum

Erfahrenes Mitglied
Hallo Norbert, hier eine neue Version mit folgender Verbesserung: Nach einer Suche kann jetzt eine neue gestartet werden, indem man den Suchbegriff im Eingabefeld ändert und Enter drückt.
Code:
    <script>
        function undoSearch() {
            // Ueber alle Fundstellen:
            document.querySelectorAll('span.found').forEach(item => {
                // Elternelement ermitteln
                const parent = item.parentNode;
                // Textknoten mit Text des Elternelementes erzeugen
                const newTxtNode = document.createTextNode(parent.textContent);
                // ... und Elternelement damit ersetzen
                parent.replaceWith(newTxtNode);
                // Jetzt hat der Knoten wieder der ursprünglichen Zustand
            });
        }
        function search(node, needle) {
            const regex = new RegExp('(' + needle + ')', 'i');
            node.childNodes.forEach(node => {
                switch (node.nodeType) {
                    // Handelt es sich um einen Elementknoten?
                    case 1:
                        // Suche fortsetzen
                        search(node, needle);
                        break;
                    // Handelt es sich um einen Textknoten?
                    case 3:
                        // Den Text heraus ziehen
                        const txt = node.textContent.trim();
                        // console.log(txt)
                        if (txt != '') {
                            // Um den gefundenen Text hervor zu heben, betten wir ihn ein
                            // span-Element ein, das wir dann geeignet mit CSS gestalten koennen.
                            // In einem Textknoten wird jedoch kein HTML interpretiert.
                            // Daher erzeugen wir ein neues span-Element und tragen dort den
                            // geaenderten Text ein.
                            let newEle = document.createElement('span');
                            newEle.innerHTML = txt.replace(regex, '<span class="found">$1</span>')
                            node.replaceWith(newEle);
                            break;
                        }
                }
            });
        }
        let lastSearchStr = '',
            foundElems,
            idxSearch = 0;
        // Die folgende Funktion wird durch das Suchformular aufgerufen
        // und startet die Suche
        function suchen(needle) {
            // event.preventDefault();
            // Unterscheidet sich der Suchstring vom gespeicherten?
            // D. h. handelt es sich um eine neue Suche?
            if (needle != lastSearchStr) {
                lastSearchStr = needle;
                // Aenderungen am DOM von der vorigen Suche rueckgaengig machen
                undoSearch();
                // Suche starten
                search(document.querySelector('body'), needle);
                // Gefundene Element bereit stellen
                foundElems = document.querySelectorAll('.found');
                // Erstes gefundenes Element hervor heben
                foundElems[idxSearch].classList.add('highlight');
                // ... und in den sichtbaren Bereich scrollen
                foundElems[idxSearch].scrollIntoView();
                searchDone = true;
            } else {
                // Hevorhebung des alten gefundenen Elementes löschen
                foundElems[idxSearch].classList.remove('highlight');
                // Naechstes gefundenes Element hervor heben
                idxSearch++;
                foundElems[idxSearch].classList.add('highlight');
                // ... und in den sichtbaren Bereich scrollen
                foundElems[idxSearch].scrollIntoView();
            }
            return false;
        }
    </script>

Übrigens: Das Skript setzt zwei Klassen: .found bei allen Fundstellen und .highlight bei der, die man gerade mit Enter angesteuert hat. Kannst Du z. B. mit diesem CSS testen:
Code:
    <style>
        .found {
            background-color: lightsalmon !important;
        }

        .found.highlight {
            background-color: lightblue !important;
        }
    </style>
 

Sempervivum

Erfahrenes Mitglied
... wie ich sehe, funktioniert nur das untere Suchfeld. Bei dem oberen fehlt der Aufruf der Suchen-Funktion beim Submit, diesen müsstest Du ergänzen:
Oberes Suchformular:
<form name="search">
Unteres Suchformular:
<form onsubmit="return suchen(this.suchtexting.value);" name="search">

Dann gibt es allerdings noch ein kleines Problem: Da sind oben zwei Menüs und wenn die zugeklappt sind und darin der Suchbegriff gefunden wird, sieht man die Fundstellen nicht. Wie hat das eigentlich bei der alten Suche funktioniert :grübel: ?
 

Webhufi

Erfahrenes Mitglied
Donnerwetter, da hast du aber was entdeckt! Ich wusste gar nicht mehr, dass unten auch noch ein Suchfeld ist... Das ist völlig unnötig, da man ja mit den Buttons nach oben gelangen kann.
Das obere habe ich rausgeworfen und das untere dorthin eingefügt, mit deiner Ergänzung <form onsubmit="return suchen(this.suchtexting.value);" name="search">.
Trotzdem funktioniert es nicht! Wobei übrigens im oberen Formular durchaus das <form name="search"> drinne war.

Zu deinem *grübel*:

Anno tobak hatte ich diese Funktion auf einer einfacheren Seite mit nur vier Hauptmenüpunkten.

Wenn man aber jetzt nur die relevante Page aufsucht, ist das Hauptmenü ja zugeklappt, sollte also kein Hindernis sein für die Suche; das zweite Menü verschwindet ebenfalls in einem sehr kleinen Fenster, wobei nur noch "Menü" übrig bleibt. Auch das sollte kein Hindernis sein für die Ergebnisse der Suchfunktion.

Was mich aber völlig stutzig macht ist ja, dass es einmal zu meiner riesigen Freude geklappt hatte! Ich hatte am oberen Bildschirmrand die erste hellblaue Fundstelle gesehen, und mit Enter gelangte ich zu nächsten. Nach dem nächsten Update ging überhaupt nichts mehr, auch jetzt nicht.
Ich habe inzwischen alle deine Codes noch einmal nacheinander probiert; erfolglos... Ich finde den gepfefferten Hasen nicht...

Änderung ist online.

Ungeschickterweise läuft der Besucherzähler weiter, aber kein traut sich zu sagen, dass da was nicht stimmt! Vielleicht stolpern auch einige darüber, wenn sie nach der gleichnamigen Zeitung suchen und klicken dann erschrocken wieder zurück... :cool:
 

Sempervivum

Erfahrenes Mitglied
Da fehlte jetzt im form-Tag das onsubmit mit dem Aufruf der Funktion suchen. Wir lassen das jedoch, ich habe statt dessen einen Eventlistener registriert, damit kann das form-Tag bleiben wie ist.

Code:
    <script>
        function undoSearch() {
            // Ueber alle Fundstellen:
            document.querySelectorAll('span.found').forEach(item => {
                // Elternelement ermitteln
                const parent = item.parentNode;
                // Textknoten mit Text des Elternelementes erzeugen
                const newTxtNode = document.createTextNode(parent.textContent);
                // ... und Elternelement damit ersetzen
                parent.replaceWith(newTxtNode);
                // Jetzt hat der Knoten wieder der ursprünglichen Zustand
            });
        }
        function search(node, needle) {
            const regex = new RegExp('(' + needle + ')', 'i');
            node.childNodes.forEach(node => {
                switch (node.nodeType) {
                    // Handelt es sich um einen Elementknoten?
                    case 1:
                        // Suche fortsetzen
                        search(node, needle);
                        break;
                    // Handelt es sich um einen Textknoten?
                    case 3:
                        // Den Text heraus ziehen
                        const txt = node.textContent.trim();
                        // console.log(txt)
                        if (txt != '') {
                            // Um den gefundenen Text hervor zu heben, betten wir ihn ein
                            // span-Element ein, das wir dann geeignet mit CSS gestalten koennen.
                            // In einem Textknoten wird jedoch kein HTML interpretiert.
                            // Daher erzeugen wir ein neues span-Element und tragen dort den
                            // geaenderten Text ein.
                            let newEle = document.createElement('span');
                            newEle.innerHTML = txt.replace(regex, '<span class="found">$1</span>')
                            node.replaceWith(newEle);
                            break;
                        }
                }
            });
        }
        let lastSearchStr = '',
            foundElems,
            idxSearch = 0;
        // Die folgende Funktion wird durch das Suchformular aufgerufen
        // und startet die Suche
        function suchen(needle) {
            // event.preventDefault();
            // Unterscheidet sich der Suchstring vom gespeicherten?
            // D. h. handelt es sich um eine neue Suche?
            if (needle != lastSearchStr) {
                lastSearchStr = needle;
                // Aenderungen am DOM von der vorigen Suche rueckgaengig machen
                undoSearch();
                // Suche starten
                search(document.querySelector('body'), needle);
                // Gefundene Element bereit stellen
                foundElems = document.querySelectorAll('.found');
                // Erstes gefundenes Element hervor heben
                foundElems[idxSearch].classList.add('highlight');
                // ... und in den sichtbaren Bereich scrollen
                foundElems[idxSearch].scrollIntoView();
                searchDone = true;
            } else {
                // Hevorhebung des alten gefundenen Elementes löschen
                foundElems[idxSearch].classList.remove('highlight');
                // Naechstes gefundenes Element hervor heben
                idxSearch++;
                foundElems[idxSearch].classList.add('highlight');
                // ... und in den sichtbaren Bereich scrollen
                foundElems[idxSearch].scrollIntoView();
            }
            return false;
        }
        document.querySelector('form[name="search"]').addEventListener('submit', function (event) {
            event.preventDefault();
            suchen(document.querySelector('input[name="suchtexting"]').value);
        });
    </script>
 

Sempervivum

Erfahrenes Mitglied
Das liegt daran, dass das Javascript im Head liegt. Auch kein Problem, wenn wir einen Eventlistener für DOM-ready nehmen:
Code:
    <script>
        function undoSearch() {
            // Ueber alle Fundstellen:
            document.querySelectorAll('span.found').forEach(item => {
                // Elternelement ermitteln
                const parent = item.parentNode;
                // Textknoten mit Text des Elternelementes erzeugen
                const newTxtNode = document.createTextNode(parent.textContent);
                // ... und Elternelement damit ersetzen
                parent.replaceWith(newTxtNode);
                // Jetzt hat der Knoten wieder der ursprünglichen Zustand
            });
        }
        function search(node, needle) {
            const regex = new RegExp('(' + needle + ')', 'i');
            node.childNodes.forEach(node => {
                switch (node.nodeType) {
                    // Handelt es sich um einen Elementknoten?
                    case 1:
                        // Suche fortsetzen
                        search(node, needle);
                        break;
                    // Handelt es sich um einen Textknoten?
                    case 3:
                        // Den Text heraus ziehen
                        const txt = node.textContent.trim();
                        // console.log(txt)
                        if (txt != '') {
                            // Um den gefundenen Text hervor zu heben, betten wir ihn ein
                            // span-Element ein, das wir dann geeignet mit CSS gestalten koennen.
                            // In einem Textknoten wird jedoch kein HTML interpretiert.
                            // Daher erzeugen wir ein neues span-Element und tragen dort den
                            // geaenderten Text ein.
                            let newEle = document.createElement('span');
                            newEle.innerHTML = txt.replace(regex, '<span class="found">$1</span>')
                            node.replaceWith(newEle);
                            break;
                        }
                }
            });
        }
        let lastSearchStr = '',
            foundElems,
            idxSearch = 0;
        // Die folgende Funktion wird durch das Suchformular aufgerufen
        // und startet die Suche
        function suchen(needle) {
            // event.preventDefault();
            // Unterscheidet sich der Suchstring vom gespeicherten?
            // D. h. handelt es sich um eine neue Suche?
            if (needle != lastSearchStr) {
                lastSearchStr = needle;
                // Aenderungen am DOM von der vorigen Suche rueckgaengig machen
                undoSearch();
                // Suche starten
                search(document.querySelector('body'), needle);
                // Gefundene Element bereit stellen
                foundElems = document.querySelectorAll('.found');
                // Erstes gefundenes Element hervor heben
                foundElems[idxSearch].classList.add('highlight');
                // ... und in den sichtbaren Bereich scrollen
                foundElems[idxSearch].scrollIntoView();
                searchDone = true;
            } else {
                // Hevorhebung des alten gefundenen Elementes löschen
                foundElems[idxSearch].classList.remove('highlight');
                // Naechstes gefundenes Element hervor heben
                idxSearch++;
                foundElems[idxSearch].classList.add('highlight');
                // ... und in den sichtbaren Bereich scrollen
                foundElems[idxSearch].scrollIntoView();
            }
            return false;
        }
        document.addEventListener('DOMContentLoaded', function () {
            document.querySelector('form[name="search"]').addEventListener('submit', function (event) {
                event.preventDefault();
                suchen(document.querySelector('input[name="suchtexting"]').value);
            });
        });
    </script>
 

Webhufi

Erfahrenes Mitglied
Wie gelassen du das alles nimmst, Ulrich... Jedenfall liest es sich so! :)

Und: Du bist 'ne Wucht! Es funktioniert tadellos! *hüpf

Na ja, fast: Ein nicht gefundenes Suchwort wird nicht als solches kommentiert.

Mir fällt auch auf, dass - wenn ich nach "urknall" suche - der erste Artikel gar nicht durchsucht wird. Liegt das vielleicht daran, dass ich schon einmal danach gesucht hatte, und diese Suche weiter unten auf der Seite fortgeführt wird oder auch gar nicht mehr? Ich finde das etwas verwirrend für den (Be)Sucher. Möglicherweise wäre es sinnvoller, die Historie zu löschen. Oder aber, was sicher aufwändig ist, den Sucher zu fragen: Suche fortsetzen? Suche von neuem Beginnen? Vielleicht in Verbindung mit dem folgenden Absatz:

Ach ja, jetzt werde ich fast unverschämt vor lauter Freude: Wenn die gesamte Seite mit Enter durchsucht wurde, tut sich beim weiteren Enter nichts mehr. Könnte man dann einen Hinweise geben, dass die Suche beendet ist und wieder an den Anfang springen? Vielleicht sogar an den Anfang des zweiten Menüs? Das wäre super.

Eigentlich genügt das blaue Highlight, ich habe das CSS korrigiert.

Noch ein "ach ja": Es ist doch üblich, dass der Autor einer solchen Zauberei genannt wird. Wo darf ich dich unterbringen, mit vielleicht einem Link?

Zum Schluss werde ich noch ein png mit einer Lupe hinter das Suchfeld einfügen. Ich denke, das kommt gut. Ich hoffe, dass ich das schaffe...
 
Status
Dieses Thema wurde gelöst! Zur Lösung gehen…