-
Notifications
You must be signed in to change notification settings - Fork 7
/
datalist-ajax.min.js
1 lines (1 loc) · 3.06 KB
/
datalist-ajax.min.js
1
class AutoComplete extends HTMLElement{constructor(){super(),this.cache={},this.url='',this.param={},this.inputdelay=300,this.querymin=1,this.optionmax=20,this.valid='',this.lastQuery=''}static get observedAttributes(){return['id','api','resultdata','resultname','inputdelay','querymin','optionmax','valid','storekeys']}attributeChangedCallback(t,e,i){if(e!==i&&(this[t]=i,'api'===t)){this.param={};for(const t of this.api.matchAll(/\$\{(.+?)\}/g)){const e=document.getElementById(t[1].trim());e&&(this.param[t[0]]=e)}}}connectedCallback(){if(this.input=this.firstElementChild,!this.input)return;const t=(this.input.name||this.input.id)+'_list',e=document.createElement('datalist');let i;e.id=t,this.datalist=this.insertBefore(e,this.input),this.input.setAttribute('autocomplete','off'),this.input.setAttribute('list',t),this.inputHandler=t=>{clearTimeout(i),i=setTimeout((()=>this.runQuery(t)),this.inputdelay)},this.input.addEventListener('input',this.inputHandler),this.changeHandler=()=>{const t=this.isValid()||this.reset||{};this.input.setCustomValidity(t?'':this.valid),this.input.checkValidity();const e={};for(const i in t)Array.from(this.input.form.querySelectorAll(`[data-autofill="${i}"], [data-autofill="${this.id}.${i}"]`)).forEach((s=>{s.value=t[i]||'',e[i]=''}));const i=new CustomEvent('autofill',{detail:t});this.dispatchEvent(i),this.reset=e},this.input.addEventListener('change',this.changeHandler)}disconnectedCallback(){this.input.removeEventListener('input',this.inputHandler),this.input.removeEventListener('blur',this.changeHandler),this.input.removeAttribute('list'),this.datalist.remove()}runQuery(){const t=this.input.value.trim().toLowerCase(),e=this.isValid(),i=this.lastQuery,s=i&&this.cache[i];let a=this.api||'';for(const t in this.param)a=a.replaceAll(t,(this.param[t].value||'').trim());!this.api||e||t.length<this.querymin||s&&s.complete&&s.url===a&&t.startsWith(i)||(this.cache[t]&&this.cache[t].url===a?this.datalistUpdate(t):(this.cache[t]={},fetch(a).then((t=>t.json())).then((e=>{if(!e)return;this.resultdata&&(e=this.getNestedKeys(e,this.resultdata)),e=Array.isArray(e)?e:[e];const i=document.createDocumentFragment();let s=this.optionmax;for(let a=0;a<e.length&&s;a++){const n=this.resultname?this.getNestedKeys(e[a],this.resultname):e[a],r=[];if(this.storekeys){const t=this.storekeys.split(',').map((t=>t.trim()));for(const i of t){const t=this.getNestedKeys(e[a],i);void 0!==t&&(r[i]=t)}}if(n&&n.toLowerCase().includes(t)){const t=document.createElement('option');t.value=n;for(const e in r)t.setAttribute('data-'+e,r[e]);i.appendChild(t),s--}}this.cache[t]={url:a,data:e,frag:i,complete:s>0},this.datalistUpdate(t)}))))}datalistUpdate(t){const e=this.cache[t];e&&e.frag&&(this.lastQuery=t,this.datalist.replaceChildren(e.frag.cloneNode(!0)))}isValid(){const t=this.input.value.trim();if(t&&this.lastQuery)return this.cache[this.lastQuery].data.find((e=>t===this.getNestedKeys(e,this.resultname)))}getNestedKeys(t,e){if(e in t)return t[e];const i=e.split('.');let s=t;for(let t=0;t<i.length&&(s=s[i[t]],void 0!==s);t++);return s}}window.customElements.define('auto-complete',AutoComplete);