"Find in window" visual alert on starting the search over again



  • Implementation of https://forum.vivaldi.net/post/177003: Searching should alert when the first match (where the search began) reached again

    screencast

    See

    custom.js

    var DEBUG = false;
    
    (function observeFindInPageInstanceCreated() {
        DEBUG && console.log('observeFindInPageInstanceCreated');
        var webviewContainer = document.querySelector('#webview-container');
        if (webviewContainer === null) {
            setTimeout(observeFindInPageInstanceCreated, 300);
            return;
        }
        webviewContainerObserver.observe(webviewContainer, {
            characterData: false,
            attributes: false,
            childList: true,
            subtree: true
        });
    })();
    
    var webviewContainerObserver = new MutationObserver(
        function (mutations) {
            mutations.forEach(function (mutation) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    mutation.addedNodes.forEach(function (node) {
                        DEBUG && console.log('webviewContainer mutation: addedNode: ', node);
                        if (node.classList.contains('find-in-page')) {
                            node.alertParentNode = getClosestParentByClass(node, 'webpageview');
                            observeFindInPageChanged.observe(node, {
                                characterData: true,
                                attributes: true,
                                childList: false,
                                subtree: true
                            });
                        }
                    });
                }
            });
        }
    );
    
    var observeFindInPageChanged = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            DEBUG && console.log('findInPageResults mutation:', mutation.type, mutation.target);
            if (mutation.target.id === 'fip-input-text') {
                saveInitialState(mutation.target);
            } else if (mutation.type === 'characterData') {
                alertIfStartedOver(mutation.target);
            }
        });
    });
    
    function saveInitialState(fipInputText) {
        setTimeout(function waitUntilAllMatchCounted() {
            saveInitialValues(fipInputText);
        }, 100);
    }
    
    function saveInitialValues(fipInputText) {
        var fip = getClosestParentByClass(fipInputText, 'find-in-page');
        fip.alertInitialCounter = fip.querySelector('.fip-results').textContent;
        fip.alertSearchFor = fipInputText.value;
        DEBUG && console.log('saveInitialValues', fip.alertInitialCounter, fip.alertSearchFor);
    }
    
    function alertIfStartedOver(results) {
        DEBUG && console.log('alertIfStartedOver', results);
        var counter = results.textContent;
        // Text object has no classList, so parent has to be passed.
        var fip = getClosestParentByClass(results.parentNode, 'find-in-page');
        var fipInputText = fip.querySelector('#fip-input-text');
        var newFindEntryOpenedWithoutChangingText = fip.alertSearchFor === undefined;
        if (newFindEntryOpenedWithoutChangingText) {
            saveInitialState(fipInputText);
            return;
        }
        if ((counter === fip.alertInitialCounter) &&
            (fipInputText.value === fip.alertSearchFor) &&
            (!fip.alertParentNode.contains(fip.alertParentNode.alert))) {
            showAlertOnNode(fip.alertParentNode);
        }
    }
    
    function showAlertOnNode(node) {
        var alertMessageContainer = document.createElement('div');
        alertMessageContainer.className = 'find-in-page-alert';
        var alertMessage = document.createElement('div');
        alertMessage.textContent = 'Searching started over';
        alertMessageContainer.appendChild(alertMessage);
        node.alert = node.appendChild(alertMessageContainer);
        setTimeout(function () {
            node.removeChild(node.alert);
        }, 3000);
    }
    
    function getClosestParentByClass(node, className) {
        while (!(node.classList.contains(className))) {
            node = node.parentNode;
        }
        return node;
    }
    

    custom.css

    .find-in-page-alert {
        display: flex;
        position: absolute;
        z-index: 1;
        width: 100%;
        top: 60%;
        justify-content: center;
        animation: pop 400ms;
    }
    
    .find-in-page-alert div {
        display: flex;
        flex: 0 0 auto;
        padding: 12px;
        margin: 8px;
        border-radius: var(--radius);
        background-color: var(--colorHighlightBg);
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    }
    

    ORIGINAL POST

    Implementation of https://forum.vivaldi.net/post/177003: Searching should alert when the first match (where the search began) reached again and/or when jumping to the top of the page

    For now it highlights the counter in "find in page" entry on the last match.

    custom.js

    var findInPageResultsObserver = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            console.log('MUTATION', mutation.type);
    
            alertIfLooped(mutation.target);
        });
    });
    
    (function alert_on_find_in_page_restart() {
        // Ugly loop to detect every "instance" of find entry:
        // If the find window is closed and opened again, then the observer is not
        // listening it without this.
        var findInPageResults = document.querySelector(".fip-results");
        if (findInPageResults !== null) {
            console.log('FIND ENTRY IS VISIBLE');
            // Observe innerHTML (characterData) change
            // It should be called only once per instance, but I cannot found a
            // way to list observers of node.
            findInPageResultsObserver.observe(findInPageResults, {characterData: true, attributes: false, childList: false, subtree: true});
        }
        setTimeout(alert_on_find_in_page_restart, 300);
    })();
    
    function alertIfLooped(results) {
        console.log('MAIN', results);
        counter = results.textContent.split(' / ')
        if (counter[0] === counter[1]) {
            console.log('ALERT ON LAST MATCH');
            results.parentNode.style.backgroundColor = 'red';
        } else {
            results.parentNode.style.backgroundColor = null;
        }
    }
    

    There is an ugly main loop, because:

    • I cannot add event for destroying the "find in page" node (to start the main loop again)
    • I cannot add observer to a non-existent node (adding .fip-results to the page should trigger an observer listener to add)

    What is the proper way to add the observer to a node which created and destroyed several times (.fip-results)?



  • In place of an ugly main loop, could you not add a MutationObserver that looks for creations of a find-in-page bar? That could be initialised each time a window is created.



  • Thanks for the tip! It became longer, but cleaner and works in all tiles.:

    var webviewContainerObserver = new MutationObserver(
        function(mutations){
            mutations.forEach(function(mutation){
                if(mutation.type === 'childList' && mutation.addedNodes.length > 0){
                    mutation.addedNodes.forEach(function (node) {
                        if(node.className === 'find-in-page') {
                            initFindBoxObserver(node);
                        }
                    });
                }
            });
        }
    );
    var webviewContainerObserverConfig = {
        characterData: false,
        attributes: false,
        childList: true,
        subtree: true
    };
    
    (function observeWebviewContainer() {
        console.log('observeWebviewContainer');
        var webviewContainer = document.querySelector('#webview-container');
        if (webviewContainer === null) {
            setTimeout(observeWebviewContainer, 300);
            return;
        }
        webviewContainerObserver.observe(webviewContainer, webviewContainerObserverConfig);
    })();
    
    var findInPageResultsObserver = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            alertOnStartingOver(mutation.target);
        });
    });
    var findInPageResultsObserverConfig = {
        characterData: true,
        attributes: false,
        childList: false,
        subtree: true
    };
    
    function initFindBoxObserver(target) {
        console.log('initFindBoxObserver', target);
        findInPageResultsObserver.observe(target, findInPageResultsObserverConfig);
    }
    
    function alertOnStartingOver(results) {
        console.log('alertOnStartingOver', results);
        counter = results.textContent.split(' / ');
        if (counter[0] === counter[1]) {
            results.parentNode.style.backgroundColor = 'red';
        } else {
            results.parentNode.style.backgroundColor = null;
        }
    }
    

    Any suggestion to improve readability?

    As I said, I'm a newbie in Vivaldi modding, plus I just geting familiar with Javascript, so there is a lot of things to learn. Any suggestions to kickstarting me?



  • If getting started, the best way is to do exactly what you're doing here: experiment with new things.

    For javascript, W3Schools and MDN are really good resources. You can also read this js cheatsheet which is a nice quick reference with useful tips.

    For the mod you've built, I would suggest:

    • Removing console.log calls from released code, you only really need it when you're doing development.
    • This might not be the same in your browser, but on mine the line counter = results.textContent.split(' / '); would best be replaced with counter = results.textContent.split(' of '); because the text shows as "3 of 3". That might be different between versions, OSes and languages, I'm not quite sure.
    • It might be worth investigating having a little notice in text that might better explain you've reached the final result on the page, in addition to the colour, as relying on colour alone to convey information isn't guaranteed to work.

    I hope these ideas are helpful. :)



  • @LonM Thanks for info, it was helpful: I did it working, please test it (code is in the opening post)!



  • It seems to mostly work. One issue I've found is that it looks for the match to the starting result before displaying the alert. The problem with that is, the find in page doesn't always select the first result - if you're scrolled to the middle of the page, for example.



  • @lonm This is what I wanted: alert me, when I "iterated" over all of the results as the title says ("... starting the search over again"). Of course it's not 1/X in all cases, if the first result is 5/X, then it has to alert me when I'm reaching the fifth result again.

    EDIT

    I just changed the screencast to clarify the behaviour.



  • @lonm The cheatsheet is a really good resource. I started doing javascript about half a year ago, and never really got into it from a theoretical side. I'm completely solution and project oriented. This should help.

    @bimlas Hmm, your code doesn't work for me. I mean I can see the console log for starting over, but the actual alert on page isn't being triggered.



  • @luetage Are you using vanilla Vivaldi, or other mods are active too? Can you please copy the log of this:

    • Open this thread in a private window (to be sure that variables are in the initial state)
    • Press Ctrl-F
    • Type "again" in the search entry
    • Hit Enter only once


  • @luetage Did you copy the CSS too?



  • @bimlas Yeah, I always test mods clean. And sure, of course I added the css too. Launching in private window I didn't do before, but why should this be needed -- and isn't the mod unusable if it only works in a private window? Anyway, I started a private window, input your chosen search word on this very site, hit enter exactly once and started to go through the results with the arrow button. Not working.



  • @luetage Of course, it works on non-private tab, but I using it to test the code (it's the same as closing Vivaldi at all and opening again).

    What version are you using?

    Can you check please that find-in-page-alert node is created or not? Maybe it is, but it's not visible (for some reasons).

    alt text



  • @bimlas Nope, doesn't show. I used the latest javascript you posted and the css. Maybe try and copy and paste your code from here. I probably won't use this mod though, so don't break sweat over it. If I'm the only one who can't get this to work there is no issue.



  • @luetage It works for me. Under version I mean Vivaldi version (I'm using 1.14.1030.3). I feel uncomfortable because of this issue, but if this exists only on your environment, then I put it on backlog.



  • @bimlas I tried again, interestingly the version from your OP works for me. (both css and js taken from there).


Log in to reply
 

Looks like your connection to Vivaldi Forum was lost, please wait while we try to reconnect.