0

The first click isn't firing and I can't work out why.

All subsequent clicks work fine. If I reload the page, same problem: first click: nothing happens.

I've tried accessing other html elements as test in the queryselector but it doesn't make a difference.

Here's the code (I'm trying to do this in vanilla JS):

const cities = [];
fetch(endpoint)
  .then(blob => blob.json())
  .then(data =>cities.push(...data));

function findMatches(wordToMatch, cities) {
  return cities.filter(place => {
    const regex = new RegExp(wordToMatch, 'gi'); 
    return place.city.match(regex) || place.state.match(regex) 
  });
}

function displayMatches() {
  const matchArray = findMatches(this.value, cities);

  const html = matchArray.map(place => {
  const regex = new RegExp(this.value, 'gi');
  const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
  const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);

    return `
      <li>
        <span class="name">${cityName}, ${stateName}</span>
        <span class="population">${numberWithCommas(place.population)}      </span>
      </li>
    `;

  }).join(''); 
  suggestions.innerHTML = html; 

   // this only works on the second click:
  const searchResult = document.querySelectorAll('li'); 
  searchResult.forEach(el => {
    el.addEventListener('click', function(){
      console.log('hi'); 

  })})


const searchInput = document.querySelector('.search');
const suggestions = document.querySelector('.suggestions');

searchInput.addEventListener('change', displayMatches); // change only fires once user navigates away from input field!
searchInput.addEventListener('keyup', displayMatches);

}


ClaudSilv
  • 3
  • 4
  • The second click on what? Notice that you don't install a click listener before `displayMatches` is executed – Bergi Jul 27 '19 at 16:18
  • The html elements I want the listener on is generated in `displayMatches`. That's why I've included it in this funtion, at the bottom. Is that the wrong place? – ClaudSilv Jul 27 '19 at 16:22
  • Nah, that should be fine. I just meant that if `suggestions` did have any content on page load, that would not get any click listeners attached to it, since it was not generated by `displayMatches`. – Bergi Jul 27 '19 at 16:31
  • So you are saying that when you debug the code, the `el.addEventListener` is executed, but then when clicking on the list items afterwards, no log appears in the console? – Bergi Jul 27 '19 at 16:32
  • If I console log `el` (or anything else for that matter), nothing happens during the first click. The second click is logged, however. – ClaudSilv Jul 27 '19 at 16:41

1 Answers1

0

EDITED: The issue comes from the search input having focus, so the click on the suggestion will blur the input and then for some reason ignore the click event on the list item (see related discussion here). In your case using mousedown instead of click seems to fix the issue, see updated snippet below.

const cities = [];

// fetch(endpoint)
//  .then(blob => blob.json())
//  .then(data =>cities.push(...data));

// mocking fetch here
_fetch = () => {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
   resolve([
    {city: 'Los Angeles', state: 'CA', population: '4M'},
    {city: 'New York City', state: 'NY', population: '8.4M'},
    {city: 'Atlanta', state: 'GA', population: '0.5M'},
   ]);
  }, 1000);
 });
};

_fetch()
 .then(data => cities.push(...data));

function findMatches(wordToMatch, cities) {
 return cities.filter(place => {
  const regex = new RegExp(wordToMatch, 'gi'); 
  return place.city.match(regex) || place.state.match(regex);
 });
}

function displayMatches() {
 const matchArray = findMatches(this.value, cities);
 
 const html = matchArray.map(place => {
  const regex = new RegExp(this.value, 'gi');
  const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
  const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);
  
  return `
   <li>
    <span class="name">${cityName}, ${stateName}</span>
    <span class="population">${place.population}</span>
   </li>
  `;
  
 }).join('');
 
 suggestions.innerHTML = html; 
 
 // this only works on the second click:
 const searchResult = document.querySelectorAll('li'); 
 searchResult.forEach(el => {
  el.addEventListener('mousedown', e => {
   console.log(`Say hi to ${e.currentTarget.innerText}`);
  });
 });
}

const searchInput = document.querySelector('.search');
const suggestions = document.querySelector('.suggestions');
// change only fires once user navigates away from input field!
searchInput.addEventListener('change', displayMatches);
searchInput.addEventListener('keyup', displayMatches);
<input type="text" class="search" />
<ul class="suggestions">

</ul>
exside
  • 3,736
  • 1
  • 12
  • 19
  • Thanks, I've just tried that but it still won't work with my code. Could this somehow be related to the fetch API as the problem only occurs upon page reload? When I run your code snippet, it all works fine! – ClaudSilv Jul 27 '19 at 16:39
  • @ClaudSliv could reproduce it when adding the search input, the issue seems to be a conflict beteween the blur and click event, see updated snippet above for a solution! – exside Jul 27 '19 at 17:37
  • Can't thank you enough! Using mousedown (though not ideal) fixed the issue. – ClaudSilv Jul 27 '19 at 17:57
  • Yes I know =/, less than optimal, but couldn't find a better solution myself, the SO post linked above has different approaches, but all have downsides or are overly complicated, therefore I just went for that "simple" fix here, but if you run into issues with keyboard etc. you might need to look for a better solution! – exside Jul 27 '19 at 17:59