How show easyautocomplete dialog only if cursor is in input

只愿长相守 提交于 2020-06-28 19:41:31

问题


I'm trying set easyautocomplete on my input. Dataset i am geting from ajax json and there is some delay. If user is too fast and writes for example "Adam" and pushes tab, cursor skips to next input, but after easyautocomplete shows dialog on previous input and doesn´t hide it. Is there any way how to show easyautocomplete dialog only when i have cursor in input?

var options = {
    minCharNumber: 3,
    url: function(phrase) {
        return "data?q=" + phrase;
    },

    getValue: function(element) {

        return element.surname + " " + element.name;
    },

    template: {
        type: "description",
        fields: {
            description: "phone"
        }
    },

    ajaxSettings: {
        dataType: "json",
        method: "POST",
        data: {
            dataType: "json"
        }
    },

    list: {
        onClickEvent: function() {
            /*Some action*/
        },
        hideAnimation: {
            type: "slide", //normal|slide|fade
            time: 400,
            callback: function() {}
        }
    },
    requestDelay: 400
};

$(".autoComplete").easyAutocomplete(options);

回答1:


Minimum, Complete, Verifiable, Example

In order to easily see this result, you'll have to open up your dev tools and throttle your network traffic so the ajax connection takes a little while

Here's a Demo of the issue in jsFiddle

Throttle Network

Handling Library Events (Doesn't Work)

My initial thought was you could handle this during some of the EAC lifecycle events that fired, like the onLoadEvent or onShowListEvent:

var options = {
  url: "https://raw.githubusercontent.com/KyleMit/libraries/gh-pages/libraries/people.json",
  getValue: "name",
  list: {   
    match: {
      enabled: true
    },
    onLoadEvent: function() {
      console.log('LoadEvent', this)
    },
    onShowListEvent: function() {
      console.log('ShowListEvent', this)
    }
  },
};

However, these methods don't seem to provide an option to alter the control flow and prevent future events

Updating Source Code (Works)

Peeking into the library's source code, Easy AutoComplete does the following ...

  1. Handles keyup events

  2. Which then calls loadData

  3. Which fires an AJAX request with the provided URL

    depending on the network and server speed, any amount of time can pass before step 4, and the input could lose focus

  4. Once the ajax promise is returned, will call showContainer()

  5. Which triggers the "show.eac" event on the container

  6. Which then opens the list with the selected animation

During step 6, we could add one last check to confirm the selected input still has focus before actually opening, which would look like this:

$elements_container.on("show.eac", function() {

  // if input has lost focus, don't show container
  if (!$field.is(":focus")) {return}

  // ... rest of method ... 

Here's a working demo in Plunker which modifies the library's source code in a new file

Unfortunately, that's not a great practice as it leaves you fragile to future changes and transfers ownership of the lib maintenance to your codebase. But it does work.

I created Pull Request #388 with the proposed changes, so hopefully a long term fix within the library itself will be available at some point in the future.

Wrapper (Recommended for now)

If you don't want to muck with third party code, there are some weird workarounds to mess with the internal execution. We can't modify the showContainer method since it's inside a closure.

We can add another listener on the show.eac event so we can be a part of the event pipeline, however there are some challenges here too. Ideally, we'd like to fire before the library handler is executed and conditionally stop propagation. However, initializing EAC will both a) create the container we have to listen to and also b) attach an event listener.

Note: Event handlers in jQuery are fired in the order they are attached!

So, we have to wait until after the lib loads to attach our handler, but that means we'll only fire after the list is already displayed.

From the question How to order events bound with jQuery, we can poke into the jQuery internals and re-order attached events so we can fire our handler before the library's handler is called. That'll look like this:

$._data(element, 'events')["show"].reverse()

Note: Both e.stopPropagation() and e.preventDefault() won't work since they prevent future events; instead we need to take more immediate action with e.stopImmediatePropagation()

So the wrapper would look like this:

$("#countries").easyAutocomplete(options);

$(".easy-autocomplete-container").on("show.eac", function(e) {
  var inputId = this.id.replace('eac-container-','')
  var isFocused = $("#"+inputId).is(":focus")
  if (!isFocused ) {
     e.stopImmediatePropagation()
  }
});

$(".easy-autocomplete-container").each(function() {
  $._data(this, 'events')["show"].reverse()
})

Here's a working demo in CodePen



来源:https://stackoverflow.com/questions/56958864/how-show-easyautocomplete-dialog-only-if-cursor-is-in-input

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!