Hogan
Hogan

Reputation: 11

Mobile Onscreen Keyboard Closing Prevents ScrollIntoView

I've created a simple search bar using a text input field.

An onkeyup event handles the searching of results and displays relevant matches directly beneath the search bar. For each of the returned search results, an onclick event is used to select the result. The result of this being that a relevant element is unhidden from a list further down the page, and scrollIntoView is used to display this element to the user.

On desktop, everything works without issue. On mobile when selecting the input field, this brings up the on-screen keyboard, and I believe the presence of this is interfering with scrollIntoView executing correctly.

Note that this issue is present whilst testing with Chrome for Android, although I will need to ensure that behaviour works as expected across all mobile browsers.

Selecting a search result whilst the on-screen keyboard is present does execute all other code correctly, in that the relevant element is unhidden from the list further down the page (if you scroll down to see it manually). When the search result is selected, the on-screen keyboard disappears as well, but the page view does not change and we remain at the top of the page.

Manually closing the on-screen keyboard (after typing into the input field but before selecting a search result) then selecting the search result, results in scrollIntoView executing as expected.

This seems as though it would be quite common to select an element whilst the on-screen keyboard is present and expect scrolling called by JS, but all my searching only seems to throw up discussions on how to hide/display the on-screen keyboard.

Any ideas on this?

Input Field (HTML):

<input id="searchBar" name="searchBar" placeholder="Type To Search" onkeyup="searchBarSearchForItem();" onfocus="searchBarSearchForItem();" class="">

Search Bar Results (HTML/PHP):

<div id="searchBarResultsContainer" class="hidden">
  <ul>
    <li id="searchBarResult-NoResults" class="">No Results</li>
      <? foreach ($allItems as $item) { ?>
        <li id="searchBarResult-<?=$item->code;?>" onclick="searchBarSelectProduct('<?=$item->code;?>');" class="hidden">
          <div class="flex">
            <p class=""><?=$item->name;?></p>
            <div class="flex-grow"></div>
          </div>
        </li>
      <? } ?>
  </ul>
</div>

searchBarSelectProduct function (JS):

function searchBarSelectProduct(itemCode) {

  // Hide the results container
  let searchResultContainerElement = document.getElementById("searchBarResultsContainer");
  searchResultContainerElement.classList.add("hidden");

  // Unhide the item entry further down the page, then ScrollIntoView 
  let itemCard = document.getElementById("itemCard-" + itemCode);
  itemCard.classList.remove("hidden");
  itemCard.scrollIntoView({behavior: "smooth", block: "center", inline: "nearest"});
  
}

Upvotes: 1

Views: 606

Answers (1)

Hogan
Hogan

Reputation: 11

I found a solution to this requiring the use of JS to 'manually' close the on-screen keyboard, combined with the subsequent call of the "scrollIntoView" function being called from within a setTimeout function.

I am assuming this works because it allows for sufficient time for the keyboard to close itself allowing the scroll to work uninterrupted

The code for the manual keyboard close I took from this question here: How can I hide the Android keyboard using JavaScript?

Resulting in the following:

function hideKeyboard(element) { // Pass the input element from which the keyboard was opened

  element.setAttribute('readonly', 'readonly'); // Force keyboard to hide on input field.
  element.setAttribute('disabled', 'true'); // Force keyboard to hide on textarea field.
  setTimeout(function() {
    element.blur();  //actually close the keyboard
    // Remove readonly attribute after keyboard is hidden.
    element.removeAttribute('readonly');
    element.removeAttribute('disabled');
  }, 100);

}

And the updated searchBarSelectProduct function is:

function searchBarSelectProduct(itemCode) {

  let searchBarInput = document.getElementById("searchBar");
  hideKeyboard(searchBarInput);

  setTimeout(function() {
    // Hide the results container
    let searchResultContainerElement = document.getElementById("searchBarResultsContainer");
    searchResultContainerElement.classList.add("hidden");

    // Unhide the item entry further down the page, then ScrollIntoView 
    let itemCard = document.getElementById("itemCard-" + itemCode);
    itemCard.classList.remove("hidden");
     itemCard.scrollIntoView({behavior: "smooth", block: "center", inline: "nearest"});
  }, 100);
}

Upvotes: 0

Related Questions