GithubHelp home page GithubHelp logo

trevoreyre / autocomplete Goto Github PK

View Code? Open in Web Editor NEW
428.0 11.0 76.0 2.39 MB

Accessible autocomplete component for vanilla JavaScript and Vue.

Home Page: https://autocomplete.trevoreyre.com

License: MIT License

Dockerfile 0.25% JavaScript 79.50% CSS 5.18% Vue 12.19% HTML 2.88%
autocomplete combobox search typeahead vue vanilla-javascript accessibility

autocomplete's Introduction

trevoreyre.com

Personal website for Trevor Eyre

autocomplete's People

Contributors

ben-roth- avatar dependabot[bot] avatar digitas-git avatar iagoleao avatar jssouders avatar scttdavs avatar sebbayer avatar tarekadam avatar trevoreyre avatar versedi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

autocomplete's Issues

Not rendering and getting `this.$refs.root is undefined` error

I've tried both importing the Vue component, and just linking to the browser bundle. Either way I get the same issue

The component simply renders as <!----> and nothing is displayed at all.

And clicking anywhere on the page gives a console error from the handleDocumentClick method

TypeError: this.$refs.root is undefined

Sticky results (keep open)

For styling purposes, it would be easier to have an option to keep the results list open. What do you suggest to accomplish that?

Deferred component creation

I tired to use same method I use for some of my components:

import Autocomplete from '@trevoreyre/autocomplete-vue';
function createAutocomplete(el, options) { // el is just empty div
	let component = new Autocomplete({
		propsData: options
	});
	component.$mount(el);
	return component;
}

But have an error: "TypeError: WEBPACK_IMPORTED_MODULE_1__trevoreyre_autocomplete_vue.a is not a constructor".

Is there a way to create component dynamically (by demand) and mount to DOM element? It's preferable to have it as root Vue instance.

How to clear/reset the autocomplete?

I'm using Autocomplete from '@trevoreyre/autocomplete-vue' with version 2.1.0.

In my Vue component, I search for a user, then change same values and submit the changed user to the backend. If this succeeds I want to reset all input fields, including the autocomplete. How can this be done in a way, that also the internal state of autocomplete is empty...

Split autocomplete component into separate composable components

Instead of a single Autocomplete component, It would be better to separate the component into its individual parts, for easier overriding and styling.

<Autocomplete>
  <AutocompleteInput />
  <AutocompleteResults>
    <AutocompleteResult>Result 1</AutocompleteResult>
    <AutocompleteResult>Result 2</AutocompleteResult>
  </AutocompleteResults>
</Autocomplete>

it is bug, using in korean

When the input value is Korean, pressing the Keydown button will call search().
So the result is reloaded and keydown doesn't work.

Perhaps in my opinion, other than English, the languages ​​are the same.

Results being displayed even if the search term is null

Hello,

First of all thanks for your component it is working great for me!

I have a small problem related to an edgy use case I have; I need to clear the input and its results and then focus to the input programmatically.

I am using Vue. I managed to do it with

this.$refs.autocomplete.value = '';
this.showResults = false;
this.$refs.autocomplete.$el.firstElementChild.children[0].focus();

I think when I focus it is triggering a search because sometimes it will show me results from what used to be the value.

This is what I mean: you can see the old result while the input is empty:

image

I also use an async answer coming from axios if it helps.

I think it is related to this: #15 "Autocomplete executes the search function on focus". I really wish I could disable this somehow.

My hacky approach that does not seem to always work is to have a showResults boolean in my Vue data, setting it to false before focusing and doing this in my search function:

            search: function(input) {
                if (input === '') {
                    return [];
                }

                return axios.get(...))
                    .then(response => {
                        if (! this.showResults) {
                            this.showResults = true;
                            return [];
                        }

                        return response.data;
                    });
            },

Investigate new API for options and results

Possibly add an options prop, which is the full list of potential options that can be chosen from, automatically filtered by the component, in addition to a results prop, which is the list of filtered results that should be shown.

Onblur execute function

Hello, I really liked the vuejs component, however I can not get the onblur to trigger an event/function like the onsubmit.
Is there any way in onblur to run the onsubmit or the same function set for it?

Render button inline next to the autocomplete

Can you provide an example of how to render a button next to the autocomplete? This seems like an common UI pattern.

I guess the root div wrapper cannot be inline-block correct?
2019-10-07_15-10-55

I was able to modify the example with default slot (full control), but does one need to write all this in order to render an inline button?

2019-10-07_15-23-21

Is there an simpler approach?
For this so common use case, would you consider adding a slot for rendering some content directly after or directly before the input element?

Clearing result after submit

I'm very new to Vue, and I cannot find a way to clear the autocomplete value after a submit request has been handled.
Any advice?

It would be a nice Prop as well.

Nuxt "countries is not defined"

Hi, im not sure why is this happening. it shows

client.js?06a0:83 ReferenceError: countries is not defined
    at VueComponent.search (sidebar.vue?480e:27)
    at AutocompleteCore.search (autocomplete.esm.js?95bf:300)
    at AutocompleteCore.eval (autocomplete.esm.js?95bf:238)
    at AutocompleteCore.eval (autocomplete.esm.js?95bf:127)
    at VueComponent.handleInput (autocomplete.esm.js?95bf:536)
    at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
    at HTMLInputElement.invoker (vue.runtime.esm.js?2b0e:2179)
    at HTMLInputElement.original._wrapper (vue.runtime.esm.js?2b0e:6917)
vue__WEBPACK_IMPORTED_MODULE_22__.default.config.errorHandler @ client.js?06a0:83
globalHandleError @ vue.runtime.esm.js?2b0e:1870
handleError @ vue.runtime.esm.js?2b0e:1839
invokeWithErrorHandling @ vue.runtime.esm.js?2b0e:1862
invoker @ vue.runtime.esm.js?2b0e:2179
original._wrapper @ vue.runtime.esm.js?2b0e:6917

the code I've written is exactly the same from the vuecomponent pages

  <div id="autocomplete">
    <autocomplete :search="search"></autocomplete>
  </div>
<script>
export default {
methods: {
    search(input) {
      if (input.length < 1) { return [] }
      return countries.filter(country => {
        return country.toLowerCase()
          .startsWith(input.toLowerCase())
      })
    }}
}
</script>

(Question) use without module bundler?

Hey Trevor,

In our project, we don't have support for Webpack, Node, ES6 modules (import/export).
I was able to integrate Vue but all my Vue components have separate Vue DOM templates.

Your autocomplete-vue package provides a single file Vue component, so my question is:
Should I use the javascript package and provide a Vue wrapper myself, or would you think it's easier to refactor you single file component into multiple files (js, html, css) ?

Unable to clear input in :keyup function

I have a :keyup function that pushes the input value to an array when enter is pressed. That part works fine. I'm trying to clear the input at the end of the function with
input.value='';
This works in the console, but not in the :keyup function. Is this a bug or something I'm doing wrong?

set the name attribute on input field

Hi there,

I'm trying to use this autocomplete vue in a traditional php form (laravel) and I need to setup the name of the input in order to the value to get passed to the form action.

There is any way of doing this? or maybe workaround?

Thank you in advance!

clearing selected value

I couldn't find an option to clear the selected value in the input, can any one guide me on doing the same?

question

Hi,

In vue.js can I bind the initial value / default value so that when my page initially loads it has the data in the autocomplete i grab from a database?

Cheers
Mark

Strict Mode

Hey, I was wondering if there is some kind of strict mode. Let's say we have a list of countries and the user starts typing germa and then hits the space bar. In this case we have germa instead of germany in the input field.
In the end I want to have the country id in a hidden field. Thats working if the user behaves as I want...but only then :)
Thanks

Option to disable native browser autocomplete - interrupts keyboard flow over elements

In Chrome the native browser autocomplete pops up over the fetched results. This causes arrow down to select the native autocomplete suggestion instead of navigating over the results from this library.

Image should be self-explanatory:
native-autocomplete-flow-off

This doesn't happen in any of your examples cause the inputs dont have any names assigned.

Chrome: Version 77.0.3865.75 (Official Build) (64-bit)
Does not happen on Firefox.

Update

Uppon further debugging this is caused when the input has name that browser is able to cache in local storage.

See reproduced issue here on example: https://codepen.io/versedi-ts/pen/eYYwqmx
Type something, press enter, refresh page and focus the input or type something in it.

TypeError: results.forEach is not a function

I'm getting a "TypeError: results.forEach is not a function" in autocomplete.cjs.js:445:13
when I try setting up a asynchronous API call to https://restcountries.eu/rest/v2/name/
The error occurs when no match is found, and I think it's because restcountries.eu returns a different JSON structure when there's no valid match.

Example call where matches are found.
https://restcountries.eu/rest/v2/name/den

Example call where NO MATCH is found.
https://restcountries.eu/rest/v2/name/qzx

Add support for debounce

I've tried to add debouncing to the search function but the component will not update after the results array is updated. I also tried to call the handleUpdate and handleShow methods directly which will show the results appropriately but clicking on any result fails to populate the event with the result object. Is there any way to update the control after a debounced API call completes? Thanks.

Add the capacity to format result

It seems like getResultValue must return a string, while it could be interesting that it is able to return HTML to allow subformatting (eg. highlighting).

I guess this should be optionable, but for instance there could be another attribute :get-result-value-html="getResultValue" which would <span v-html="getResultValueHtml(result)"></span> instead of {{getResultValue(result)}}.

The best solution would be to being able to return a component, but I have the impression that it is not possible with vue, is it?

prevent default form submit using autocomplete

I have the following modified code from docs example: https://codepen.io/trevoreyre/pen/vbqpxv

I just wrapped in in a form, since this are my requirements:

  • typing for something in the input without selecting any option from dropdown performs a normal server search (GET)
  • typing for something in the input, moving the key down (selecting one suggestion) and press ENTER -> go to the result page
<div id="app">
  <form action="www.test.com/search" method="GET">
    <autocomplete
      :search="search"
      placeholder="Search Wikipedia"
      aria-label="Search Wikipedia"
      :get-result-value="getResultValue"
      @submit="handleSubmit"
    ></autocomplete>
  </form>
</div>

my problem is that handleSubmit is called but the default form action is faster, causing the page to reload and do the server GET.

There is any way to preventDefault on the form? or another ideas?

Thank you!

Provide TypeScript definitions or add them to DefinitelyTyped

Currently there's no index.d.ts nor package @types/autocomplete-js available.

Types can be defined inside library itself in index.d.ts or added to the DefinitelyTyped repository here: https://github.com/DefinitelyTyped/DefinitelyTyped

I never created typings myself, but from what I know it would be something like this for DefinitelyTyped:

// Type definitions for autocomplete-js
// Project: https://github.com/trevoreyre/autocomplete/, https://autocomplete.trevoreyre.com/
// Definitions by: versedi <https://github.com/versedi>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3

export as namespace Autocomplete;

// --------------------------------------------------------------------------------------
// Options Interfaces
// --------------------------------------------------------------------------------------
    export interface AutocompleteOptions {
        /**
         * The search function to be executed on user input. Can be a synchronous function or a Promise.
         * @param input
         */
        search(input: string): Function|Promise<string>,

        /**
         * Controls whether first result should be highlighted after input
         * Optional
         */
        autoSelect?: boolean,

        /**
         * Sets the value of the calling component's input element
         */
        setValue? (): Function,

        /**
         * Sets attributes on the calling component's input element
         */
        setAttribute? (): Function,

        /**
         * Fired when the results list is updated. Receives results (Array), and selectedIndex (Number) as arguments.
         */
        onUpdate? (): Function,

        /**
         * Fired when user submits result. Receives result as argument.
         * @param result
         */
        onSubmit(result: object): void,

        /**
         *
         * @param result
         * @param props
         */
        renderResult(result: object, props: Props): string

        /**
         * Fired when the results list is shown
         */
        onShow? (): Function,

        /**
         * Fired when the results list is hidden
         */
        onHide? (): Function,

        /**
         * Fired if search is a Promise and hasn't resolved yet
         */
        onLoading? (): Function,

        /**
         * Fired after asynchronous search function resolves
         */
        onLoaded? (): Function,
    }




export interface EventHandlers {
    handleInput: Function,
    handleKeyDown: Function,
    handleBlur: Function,
    handleResultMouseDown: Function,
    handleResultClick: Function,
}


// --------------------------------------------------------------------------------------
// Autocomplete
// --------------------------------------------------------------------------------------

declare global {
    interface Autocomplete<TElement = HTMLElement> {
    }
}

The global declaration would probaly require some tweaking.

For self-alone index.ts that anyone can put into his typeRoots in tsconfig.json:

declare module '@trevoreyre/autocomplete' {
    export default class Autocomplete<TElement = HTMLElement> {
        constructor(inputSelector: string, options?: AutocompleteOptions);
    }

    /**
     * * Creates a props object with overridden toString function. toString returns an attributes
     string in the format: `key1="value1" key2="value2"` for easy use in an HTML string.
     */
    export class Props {
        /**
         * @param index
         * @param selectedIndex
         * @param baseClass
         */
        constructor(index: string, selectedIndex: string, baseClass: string);
        toString(): string
    }


    export interface AutocompleteOptions {
        /**
         * The search function to be executed on user input. Can be a synchronous function or a Promise.
         * @param input
         */
        search(input: string): Function|Promise<string>,

        /**
         * Controls whether first result should be highlighted after input
         * Optional
         */
        autoSelect?: boolean,

        /**
         * Sets the value of the calling component's input element
         */
        setValue? (): Function,

        /**
         * Sets attributes on the calling component's input element
         */
        setAttribute? (): Function,

        /**
         * Fired when the results list is updated. Receives results (Array), and selectedIndex (Number) as arguments.
         */
        onUpdate? (): Function,

        /**
         * Fired when user submits result. Receives result as argument.
         * @param result
         */
        onSubmit(result: object): void,

        /**
         *
         * @param result
         * @param props
         */
        renderResult(result: object, props: Props): string

        /**
         * Fired when the results list is shown
         */
        onShow? (): Function,

        /**
         * Fired when the results list is hidden
         */
        onHide? (): Function,

        /**
         * Fired if search is a Promise and hasn't resolved yet
         */
        onLoading? (): Function,

        /**
         * Fired after asynchronous search function resolves
         */
        onLoaded? (): Function,
    }



    export interface EventHandlers {
        handleInput: Function,
        handleKeyDown: Function,
        handleBlur: Function,
        handleResultMouseDown: Function,
        handleResultClick: Function,
    }
}

and autocomplete-js typings:

declare module '@trevoreyre/autocomplete-js' {
    export default class Autocomplete<TElement = HTMLElement> {
        constructor(inputSelector: string, options?: AutocompleteJsOptions);
    }
    /**
     * * Creates a props object with overridden toString function. toString returns an attributes
     string in the format: `key1="value1" key2="value2"` for easy use in an HTML string.
     */
    export class Props {
        /**
         * @param index
         * @param selectedIndex
         * @param baseClass
         */
        constructor(index: string, selectedIndex: string, baseClass: string);
        toString(): string
    }


{
        /**
         * The search function to be executed on user input. Can be a synchronous function or a Promise.
         * @param input
         */
        search(input: string): Function|Promise<string>,

        /**
         * Fired when user submits result. Receives result as argument.
         * @param result
         */
        onSubmit(result: object): void,

        baseClass?: string,

        /**
         * Controls whether first result should be highlighted after input
         */
        autoSelect?: boolean,

        /**
         * For complex search results, this function is executed to get the value to display in the input
         * @param result
         */
        getResultValue? (result: object): void,

        /**
         *
         * @param result - The result value returned from your search function
         * @param props - An object containing generated attributes for the result item, which are expected to be set on your li element. The object has a custom toString function which lets you easily serialize it to a String of HTML attributes in the form attribute1="value1" attribute2="value2". This way, you don't have to worry about generating the proper IDs, classes, and ARIA attributes yourself.
         */
        renderResult? (result: object, props: Props): string|Element
    }


}

How to use multiple autocomplete instances

how can I have multiple instances at a time in a v-for loop. Is there a way to pass an index to the submit handler along with the result? I'm struggling to find a way to handle the submit event of dynamically added autocomplete instances.

For example:

<div v-for="(airport, index) in airports">
    <autocomplete :search="airportAutocomplete" @submit="handleSubmit(result, index)"></autocomplete>
</div>

...

data() {
  return {
    airports: [
      {
        code: '',
      },
      {
          code: '',
      },
      {
        code: '',
      }
    ]
  }
},
methods: {
  handleSubmit(result, index) {
    this.airports[index].code = result.code
  }
}

Is there a way to handle not firing on every keystroke?

I would like to use this to hit an API (which I am doing successfully) but rate limits aren't infinite and every single keystroke is a call to the api. Is there a way to delay the search function for like at least one or two seconds after typing?

Get input value programmatically

In my case, I need to get the input value on the click of another button.
After inspecting the component, I'm doing that so now:

handleAddNewGuest() {
  const name = this.$refs.addAttendeeField.core.value;
}

Another approach would be to always keep a backup of the current value in my parent:

data() {
	return {
		currentInputValue: ""
	}
},
search(input) {
  this.currentInputValue = input;
}

Will I always have access to core.value with newer releases? If there is a more standard approach, then perhaps I missed it?

Thanks!

How to bind autocomplete input via v-model in the Vue component

I have a Vue component that has a data item in it; before using the Autocomplete component, I could do something like this in my component's template:

<input v-model="year">

This creates all the magic two-way bindings for me... but when I'm using the Autocomplete component, it's unclear to me how I could achieve the same results? I'm looking for something like this (p-code):

<Autocomplete v-model="year"
              :search="searchYears"
>
</Autocomplete>

I'm going for a two-way binding between my component's data, and the Autocomplete result. Would I need to do something like override inputProps somehow? Or use named slots to override the default template as per this example? If so, how?

I feel like I'm missing something, because this seems like it'd be a common use-case where we want to use v-model to set up the bindings for the Autocomplete result.

Image in result - network request and download each time element is selected - even if it was already selected before

Reproduce pen: https://codepen.io/versedi-ts/pen/eYYwqmx

The image used in result is requested every time user presses arrow down/arrow up.

  1. Would it be possible to trigger the network request only on first select? Not sure why it's triggered on the select event - the images are already downloaded by browser when autocomplete is opened so that request is completely unnecessary.

  2. Would it be possible to leverage browser caching and use the previously downloaded image? The request URL is the same, not sure why this doesn't happen out of the box.

9lpER3n

handleKeyDown: Tab not triggering submit

When using the VueComponent layer, when the AutocompleteCore handles the "Tab" keypress, it doesn't trigger the onSubmit method. The value gets filled to the input but I can't update my object when the submit event is triggered. I'm not sure if this was by design or not, but I would expect Tab and Enter to be handled similarly.

Option to show results only on modify and arrow keys instead of focus

Would be useful to have some option that defines what events should trigger search and display dropdown with results. In my case I'm trying to replicate the behavior of my old jQuery component, that was custom select with search ability. So I wolud like it to dropdown on value change or ArrowUp, ArrowDown, Enter keys.

Update value from outside

Hello, is it possible to update the input value from outside with vue js.
I use v-model, but its not seem to work.

Any idea ?

Vue - accessing DOM nodes within Vue methods

methods like search onSubmit should pass in the root node of the component or the input component. When dynamically creating autocompletes, this reference may be necessary.

Also, please create a Core Vue label for issues and maybe even a v2-patch and v3 label

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.