Solution 1 :

I think the best solution is to remove the focus listeners temporarily.
It should work using this:

(function searchFilter() {

    let input = document.getElementById('searchFilter');
    let suggestions = document.getElementsByClassName("searchFilter-suggestions")[0];

    eventListeners();

    // Add Event Listerns
    function eventListeners() {
      input.addEventListener('keyup', searchQuery);
      suggestions.addEventListener("mouseenter", () => removeInputFocusListeners());
      suggestions.addEventListener("mouseleave", () => addInputFocusListeners());
    };

    function addInputFocusListeners() {
      input.addEventListener('focusout', searchQuery);
      input.addEventListener('focusin', searchQuery);
    }

    function removeInputFocusListeners() {
      input.removeEventListener('focusout', searchQuery);
      input.removeEventListener('focusin', searchQuery);
    }

    ...

Problem :

QUESTION:
Why do I have to click twice to get links in search results menu to load page?

See here:
Type in Staff, or Blog in the filter field. You have to click on each link twice to get the page to load?
https://startech-enterprises.github.io/docs/data-integration-and-etl/branches-and-loops-local.html

I’m trying to get to this behaviour (i/e just one click):
https://learn.microsoft.com/en-us/dotnet/csharp/linq/

NOTE
The code in the link above has now been updated, based on the answers given below

CODE

JS I’m using

/**
* Search Filter
*/

"use strict";
(function searchFilter() {

    eventListeners();

    // Add Event Listerns
    function eventListeners(){
        document.getElementById('searchFilter').addEventListener('keyup', searchQuery);
        document.getElementById('searchFilter').addEventListener('focusout', searchQuery);
        document.getElementById('searchFilter').addEventListener('focusin', searchQuery);
    };

    function searchQuery(){

        // Declare variables
        let input, filter, ul_toc, li_toc, ul_suggestions, li_suggestion, a1, a2, a3, i, j, k, txtValue, txtValue2, txtValue3, link;

        input = document.getElementById('searchFilter');
        filter = input.value.toUpperCase();
        ul_toc = document.getElementsByClassName("toc")[0];
        li_toc = ul_toc.getElementsByClassName("none");
        ul_suggestions = document.getElementsByClassName("searchFilter-suggestions")[0];

        // Check whether input is empty. If so hide UL Element
        if (filter === "") {
            ul_suggestions.classList.add("is-hidden")
        };

        // Check whether input is not empty. If so show UL Element
        if (filter !== "") {
            ul_suggestions.classList.remove("is-hidden")
        };

        // Check whether input is not active. If so hide UL Element
        if (input !== document.activeElement) {
            setTimeout(function(){
            ul_suggestions.classList.add("is-hidden");
            }, 2000);
        };

        // Check whether input is active. If so show UL Element
        if (input === document.activeElement) {
        ul_suggestions.classList.remove("is-hidden")
        };

        // Keep emptying UL on each keyup event, or when input element is not active
        ul_suggestions.innerHTML = "";

        let df = new DocumentFragment(); 

        // Run search query so long as filter is not an empty string
        if(filter !== ""){
            // Loop through all list items, and update document fragment for those that match the search query
            for (i = 0; i < li_toc.length; i++) {
                a1 = li_toc[i].getElementsByTagName("a")[0];
                txtValue = a1.textContent || a1.innerText;

                if (txtValue.toUpperCase().indexOf(filter) > -1) {  

                    // Start creating internal HTML
                    li_suggestion = document.createElement('li');
                    li_suggestion.classList.add("searchFilter-suggestion");

                    // Parent span element
                    let span = document.createElement("SPAN");
                    span.className = ("is-block is-size-7 has-padding-left-small has-padding-right-small");
                    link = document.createElement('a');
                    link.href = a1.href;
                    span.appendChild(link);

                        // Child 1 span element
                        let span2 = document.createElement("SPAN");
                            span2.className = ("is-block has-overflow-ellipsis-tablet");
                            span2.textContent = txtValue;

                        // Child 2 span element
                        let span3 = document.createElement("SPAN");
                            span3.className = ("is-block has-text-subtle has-overflow-ellipsis is-size-8 has-line-height-reset has-padding-bottom-extra-small");

                            j = 0;
                            let immediateParent = li_toc[i].parentElement;
                            let correctParent = li_toc[i].parentElement;

                            // Get top most level of branch --> Set as Node 1
                            while(true){
                                if (immediateParent.parentElement.classList.contains('toc')) break;
                                immediateParent = immediateParent.parentElement;
                                j++;
                            };
                            if (j == 0){
                                a2 = li_toc[i].getElementsByTagName("a")[0];
                            } 
                            else {
                                k = 0;
                                for ( k = 0; k < j - 1; k++) {
                                    correctParent = correctParent.parentElement;
                                };

                                a2 = previousByClass(correctParent, "treeitem");
                                a2 = child_by_selector(a2, "tree-expander")
                            }
                            txtValue2 = a2.textContent || a2.innerText;
                            txtValue2 = document.createTextNode(txtValue2);

                            // Insert Chevron Right --> Set as Node 2
                            let span4 = document.createElement("SPAN");
                            span4.className = ("has-padding-right-extra-small has-padding-left-extra-small");
                            span4.innerHTML =  '&nbsp&#9002&nbsp';
                            span4.setAttribute("style", "font-size: 0.70rem; font-weight: bold");

                            // Get second-top most level of branch --> Set as Node 3
                            correctParent = li_toc[i].parentElement;
                            switch (j) {
                                case 0:
                                    a3 = "";
                                break;
                                case 1:
                                    a3 = li_toc[i].getElementsByTagName("a")[0];
                                default: {
                                    k = 0;
                                    for ( k = 0; k < j - 2; k++) {
                                        correctParent = correctParent.parentElement;
                                    };

                                    a3 = previousByClass(correctParent, "treeitem");
                                    a3 = child_by_selector(a3, "tree-expander")
                                    }
                                }   

                            if (a3 != ""){
                                txtValue3 = a3.textContent || a3.innerText;
                                txtValue3 = document.createTextNode(txtValue3);
                                span3.appendChild(txtValue2);
                                span3.appendChild(span4);
                                span3.appendChild(txtValue3);
                            } else {
                                span3.appendChild(txtValue2);
                            }
                        span.firstChild.appendChild(span2);
                        span.firstChild.appendChild(span3); 

                    li_suggestion.appendChild(span);
                    df.appendChild(li_suggestion)
                } 
            }
             // Output HTML, and remove is-hidden class
             ul_suggestions.appendChild(df);
        }
    }
})();

// WAIT TILL DOCUMENT HAS LOADED BEFORE INITIATING FUNCTIONS
document.addEventListener('DOMContentLoaded', searchFilter);

CSS I’m using:

    /* Search Filter */
    .filter-icon{
      display: inline-block;
      height:0.9rem;
      width: 1.0rem;
      text-transform: none;
      text-align: center;
    }

    .searchFilter {
      display: inline-block;
      position: relative;
    }

    .searchFilter-input {
      padding-right: 26px;
    }

    .searchFilter-suggestions {
      list-style-type: none;
      z-index: 1;
      position: absolute;
      max-height: 18rem;
      min-width: 100%;
      max-width: 100%;
      padding: 0;
      margin: 2px 0 0 !important;
      cursor: default;
      box-shadow: 0 1.6px 3.6px 0 rgba(0,0,0,0.132),0 .3px .9px 0 rgba(0,0,0,0.108);
      border: 1px solid #e3e3e3;
      background-color: white;
    }

    @media screen and (min-width: 768px), print {
      .searchFilter-suggestions {
        max-width: 500px;
      }
    }

    .searchFilter-suggestion {
      display: block;
      border: 1px solid transparent;
    }

    .searchFilter-suggestion a {
      color: rgb(23, 23, 22);
      text-decoration: none;
    } 

    .searchFilter-suggestion:hover{
      background-color: #f2f2f2;;
      border: 1px solid rgba(0,0,0,0);
    }

    .is-hidden {
      display: none !important;
    }

Relevant portion of HTML with UL that loads the search results:
(The search results document fragment generated by the JS gets loaded in the ul, with the class, searchFilter-suggestions)

form class = "has-margin-bottom-small" action="javascript:" role="search">
    <label class="visually-hidden">Search</label>
        <div class="searchFilter is-block">
            <div class="control has-icons-left">
            <input id="searchFilter" class="searchFilter-input input control has-icons-left is-full-width is-small" role="combobox" maxlength="100" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" placeholder="Filter by title" type="text">
            <span class="icon is-small is-left">
            <img src="/../docs/assets/images/filter.png" class="filter-icon">
            </span>
            </div>
        <ul class="searchFilter-suggestions is-vertically-scrollable is-hidden"></ul>
    </div>
</form>

Comments

Comment posted by Paul

What I found out so far: On the first click, the

Comment posted by Paul

Okay, I think it’s due to the fact, that you still have focus on the input element. If you click besides the input and then the link it’s working. Even though I’m wondering why

Comment posted by Sachin

I think you’re close, it’s something to do with the input element, focus in, focus out, but I can’t get it still just to work on a first click, like happens in the other site

Comment posted by Michel

Don’t know if you changed something, in Edge and Firefox it’s working ok. Just one click

Comment posted by Sachin

Sorry yes I changed the code, using Pauls solution below, so the site in the link above , will now work. The Question has been answered!

Comment posted by Sachin

Thank you. the first click functionality now works! but if you say type ‘branch’ in the filter box, let the search query load a short list,… and then click anywhere else on the page, the search list still stays there, when it should disappear?

Comment posted by Sachin

Sorry, it does disappear, I had to reduce the length of the settimeout on the focus out check

Comment posted by Paul

@Sachin I will take a look. Since I just noticed, that you don’t seem to do that in your questions: It’s nice to upvote helpful answers and mark the ones, which solve your problem as ‘best. It’s at least some little ‘achievment’ for the work the users put in to help others.

Comment posted by meta.stackexchange.com/questions/173399/…

Check this:

Comment posted by meta.stackexchange.com/questions/5234/…

And btw.: same for ‘accepting anwers’. Not sure, if you can do this with your reputation or not:

By