PHP Classes

File: js/datalist.polyfill.js

Recommend this page to a friend!
  Classes of Mehmet Kidiman  >  Developbat PHP CRUD  >  js/datalist.polyfill.js  >  Download  
File: js/datalist.polyfill.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Developbat PHP CRUD
Show pages to manage table records using Bootstrap
Author: By
Last change:
Date: 11 months ago
Size: 8,227 bytes
 

Contents

Class file image Download
// https://github.com/Fyrd/purejs-datalist-polyfill
// license: MIT
(function(document) {
    var IE_SELECT_ATTRIBUTE = 'data-datalist';
    var LIST_CLASS = 'datalist-polyfill';
    var ACTIVE_CLASS = 'datalist-polyfill__active';
    
    var datalistSupported = !!(document.createElement('datalist') && window.HTMLDataListElement);
    var ua = navigator.userAgent;
    
    // Android does not have actual support
    var isAndroidBrowser = ua.match(/Android/) && !ua.match(/(Firefox|Chrome|Opera|OPR)/);
    if( datalistSupported && !isAndroidBrowser ) {
        return;
    }
    
    var inputs = document.querySelectorAll('input[list]');
    
    var triggerEvent = function(elem, eventType) {
        var event;
        if (document.createEvent) {
            event = document.createEvent("HTMLEvents");
            event.initEvent(eventType, true, true);
            elem.dispatchEvent(event);
        } else {
            event = document.createEventObject();
            event.eventType = eventType;
            elem.fireEvent("on" + eventType, event);
        }
    };

    for( var i = 0; i < inputs.length; i++ ) {
        var input = inputs[i];
        var listId = input.getAttribute('list');
        var datalist = document.getElementById(listId);
        if( !datalist ) {
            console.error('No datalist found for input: ' + listId);
            return;
        }
        
        // Only visible to <= IE9
        var childSelect = document.querySelector('select[' + IE_SELECT_ATTRIBUTE + '="' + listId + '"]');
        var parent = childSelect || datalist;
        var listItems = parent.getElementsByTagName('option');
        convert(input, datalist, listItems);
        if( childSelect ) {
            childSelect.parentNode.removeChild( childSelect );
        }
    }
    
    function convert(input, datalist, listItems) {
        var fakeList = document.createElement('ul');
        var visibleItems = null;
        fakeList.id = listId;
        fakeList.className = LIST_CLASS;
        document.body.appendChild( fakeList );

        var scrollValue = 0;

          // Used to prevent reflow
        var tempItems = document.createDocumentFragment();
        
        for( var i = 0; i < listItems.length; i++ ) {
            var item = listItems[i];
            var li = document.createElement('li');
            li.innerText = item.value;
            tempItems.appendChild( li );
        }
        fakeList.appendChild( tempItems );
        var fakeItems = fakeList.childNodes;
        var eachItem = function(callback) {
            for( var i = 0; i < fakeItems.length; i++ ) {
                callback(fakeItems[i]);
            }
        };
        var listen = function(elem, event, func) {
            if( elem.addEventListener ) {
                elem.addEventListener(event, func, false);
            } else {
                elem.attachEvent('on' + event, func);
            }
        };
        
        datalist.parentNode.removeChild( datalist );
        
        listen(input, 'focus', function() {
            // Reset scroll
            fakeList.scrollTop = 0;
            scrollValue = 0;
        });
        
        listen(input, 'blur', function(evt) {
            // If this fires immediately, it prevents click-to-select from working
            setTimeout(function() {
                fakeList.style.display = 'none';
                eachItem( function(item) {
                    // Note: removes all, not just ACTIVE_CLASS, but should be safe
                    item.className = '';
                });
            }, 100);
        });
        
        var positionList = function() {
            fakeList.style.top = input.offsetTop + input.offsetHeight + 'px';
            fakeList.style.left = input.offsetLeft + 'px';
            fakeList.style.width = input.offsetWidth + 'px';
        };
        
        var itemSelected = function(item) {
            input.value = item.innerText;
            triggerEvent(input, 'change');
            setTimeout(function() {
                fakeList.style.display = 'none';
            }, 100);
        };
        
        var buildList = function(e) {
            // Build datalist
            fakeList.style.display = 'block';
            positionList();
            visibleItems = [];
            eachItem( function(item) {
                // Note: removes all, not just ACTIVE_CLASS, but should be safe
                var query = input.value.toLowerCase();
                var isFound = query.length && item.innerText.toLowerCase().indexOf( query ) > -1;
                if( isFound ) {
                    visibleItems.push( item );
                }
                item.style.display = isFound ? 'block' : 'none';
            } );
        };
        
        listen(input, 'keyup', buildList);
        listen(input, 'focus', buildList);
        
        // Don't want to use :hover in CSS so doing this instead
        // really helps with arrow key navigation
        eachItem( function(item) {
            // Note: removes all, not just ACTIVE_CLASS, but should be safe
            listen(item, 'mouseover', function(evt) {
                eachItem( function(_item) {
                    _item.className = item == _item ? ACTIVE_CLASS : '';
                });
            });
            listen(item, 'mouseout', function(evt) {
                item.className = '';
            });
            // Mousedown fires before native 'change' event is triggered
            // So we use this instead of click so only the new value is passed to 'change'
            listen(item, 'mousedown', function(evt) {
                itemSelected(item);
            });
        });
        
        listen(window, 'resize', positionList);
        
        listen(input, 'keydown', function(e) {
            var activeItem = fakeList.querySelector("." + ACTIVE_CLASS);
            if( !visibleItems.length ) {
                return;
            }
            
            var lastVisible = visibleItems[ visibleItems.length-1 ];
            var datalistItemsHeight = lastVisible.offsetTop + lastVisible.offsetHeight;
            
            // up/down arrows
            var isUp = e.keyCode == 38;
            var isDown = e.keyCode == 40;
            if ( (isUp || isDown) ) {
                if( isDown && !activeItem ) {
                    visibleItems[0].className = ACTIVE_CLASS;
                } else if (activeItem) {
                    var prevVisible = null;
                    var nextVisible = null;
                    for( var i = 0; i < visibleItems.length; i++ ) {
                        var visItem = visibleItems[i];
                        if( visItem == activeItem ) {
                            prevVisible = visibleItems[i-1];
                            nextVisible = visibleItems[i+1];
                            break;
                        }
                    }

                    activeItem.className = '';
                    if ( isUp ) {
                        if( prevVisible ) {
                            prevVisible.className = ACTIVE_CLASS;
                            if ( prevVisible.offsetTop < fakeList.scrollTop ) {
                                fakeList.scrollTop -= prevVisible.offsetHeight;
                            }
                        } else {
                            visibleItems[visibleItems.length - 1].className = ACTIVE_CLASS;
                        }
                    }
                    if ( isDown ) {
                        if( nextVisible ) { 
                            nextVisible.className = ACTIVE_CLASS;
                            if( nextVisible.offsetTop + nextVisible.offsetHeight > fakeList.scrollTop + fakeList.offsetHeight ) {
                                fakeList.scrollTop += nextVisible.offsetHeight;
                            }
                        } else {
                            visibleItems[0].className = ACTIVE_CLASS;
                        }
                    }
                }
            }
            
            // return or tab key
            if ( activeItem && (e.keyCode == 13 || e.keyCode == 9) ){
                itemSelected(activeItem);
            }
        });
    }
}(document));
For more information send a message to info at phpclasses dot org.