Datalist
A datalist is a drop down list of options from a form text field. Datalist support among browsers is currently very low. This function allows you to provide cross browser datalist style support to text fields.
Datalist
Datalist( elements )
function Datalist( elements )
{
var n = elements.length;
for ( var i=0;i < n; i++ )
{
var e = elements[i];
if ( Class.Contains( e, "datalist" ) ) Datalist.Setup( e );
e.addEventListener( "keydown", Datalist.KeyHandler );
}
}
Helper functions
Datalist.Setup
=
function( datalist )
{
//datalist.autocomplete = "off";
if ( datalist.hasAttribute( "data-kind" ) )
{
var kind = datalist.getAttribute( "data-kind" );
var parameters = {};
parameters.kinds = kind;
var parameter_names = datalist.getAttribute( "data-parameters" );
if ( parameter_names )
{
var obj = {};
var list = parameter_names.split( "," );
var n = list.length;
for ( var i=0; i < n; i++ )
{
var name = list[i];
obj[name] = Locations.SearchValue( name );
}
parameters.json = JSON.stringify( obj );
}
Call( "/api/multiselect/", parameters, function( responseText ) { Datalist.SetupListItems( datalist, responseText ); } );
}
else
{
Datalist.SetupFunctions( datalist );
}
datalist.handler
=
function( event )
{
// Empty default handler.
}
}
Datalist.SetupListItems
=
function( datalist, responseText )
{
var json = JSON.parse( responseText );
var kind = datalist.getAttribute( "data-kind" );
if ( "" != kind )
{
datalist.setAttribute( "data-kind", "" );
var id = datalist.getAttribute( "id" );
var ul = document.createElement( "UL" );
ul.className = "datalist_list";
ul.setAttribute( "id", id + "-div" );
ul.style.display = "none";
datalist.parentNode.insertBefore( ul, datalist.nextSibling );
datalist.sublist = ul;
datalist.onchange = null;
if ( "OK" == json.status )
{
var n = json.results.length;
for ( var i=0; i < n; i++ )
{
var tuple = json.results[i];
if ( tuple.name == kind )
{
var m = tuple.tuples.length;
/* Uncomment for a list of options that the search term is a prefix of.
for ( var j=0; j < m; j++ )
{
var li = document.createElement( "LI" );
li.innerHTML = tuple.tuples[j].text;
li.dataListItemType = "prefixed";
ul.appendChild( li );
}
*/
for ( var j=0; j < m; j++ )
{
var li = document.createElement( "LI" );
li.setAttribute( "data-name", tuple.tuples[j].name );
li.setAttribute( "data-text", tuple.tuples[j].text );
li.innerHTML = tuple.tuples[j].text;
li.dataListItemType = "contains";
ul.appendChild( li );
}
break;
}
}
}
Datalist.SetupFunctions( datalist, ul );
}
}
Datalist.SetupFunctions
=
function( datalist, ul )
{
var list_items = datalist.sublist.getElementsByTagName( "LI" );
var n = list_items.length;
for ( var i=0; i < n; i++ )
{
var li = list_items[i];
li.onmouseover = Datalist.OnMouseOver;
li.onmouseout = Datalist.OnMouseOut;
li.onclick = Datalist.OnClick;
}
datalist.oninput = function() { Datalist.OnInput ( event, datalist ); };
datalist.onfocusout = function() { Datalist.OnFocusOut( event, datalist ); };
ul.onfocusout = function() { Datalist.OnFocusOut( event, datalist ); };
}
Datalist.OutsideClick
=
function( event )
{
console.log( event );
Datalist.HideDatalists( document.getElementsByTagName( "UL" ) );
}
Datalist.OnInput
=
function( event, datalist )
{
var div = datalist.sublist;
if ( ! div.ignoreFocus )
{
div.style.display = "block";
document.addEventListener( "click", Datalist.OutsideClick );
var filter = event.target.value;
var list_items = div.getElementsByTagName( "LI" );
var n = list_items.length;
for ( var i=0; i < n; i++ )
{
var li = list_items[i];
var lcl = li.innerHTML.toLowerCase();
var lcf = filter.toLowerCase();
switch ( li.dataListItemType )
{
case "prefixed":
// Case insensitive starts With
if ( "" == lcf )
{
li.style.display = "none";
}
else
if ( 0 != lcl.indexOf( lcf ) )
{
li.style.display = "none";
}
else
{
li.style.display = "block";
}
break;
case "contains":
// Case insensitive matching
if ( "" == lcf )
{
li.style.display = "none";
}
else
if ( -1 == lcl.indexOf( lcf ) )
{
li.style.display = "none";
}
else
{
li.style.display = "block";
}
break;
}
}
}
else
{
div.ignoreFocus = false;
}
}
Datalist.OnMouse
=
function( event, selected )
{
var datalist_list = event.target.parentNode;
var list_items = datalist_list.getElementsByTagName( "LI" );
var n = list_items.length;
for ( var i=0; i < n; i++ )
{
var li = list_items[i];
RemoveClass( li, "selected" );
}
if ( selected ) AddClass( event.target, "selected" );
}
Datalist.OnMouseOver = function( event ) { Datalist.OnMouse( event, true ); }
Datalist.OnMouseOut = function( event ) { Datalist.OnMouse( event, false ); }
Datalist.OnClick
=
function( event )
{
var li = event.target;
var datalist_list = li.parentNode;
var datalist = datalist_list.previousSibling;
if ( datalist )
{
datalist.setAttribute( "data-name", li.getAttribute( "data-name" ) );
datalist.value = li.innerHTML.trim().replace( "&", "&" );
datalist.setCustomValidity( "" );
datalist_list.style.display = "none";
datalist_list.ignoreFocus = true;
if ( datalist.form.hasAttribute( "data-change-url" ) )
{
var evt = new Object();
evt.target = datalist;
Save( evt );
}
else
{
datalist.handler( datalist );
}
}
document.removeEventListener( "click", Datalist.OutsideClick );
event.stopPropagation();
}
Datalist.OnFocusOut
=
function( event, datalist )
{
var list_items = datalist.sublist.getElementsByTagName( "LI" );
var hide = true;
var n = list_items.length;
for ( var i=0; i < n; i++ )
{
var li = list_items[i];
if ( Class.Contains( li, "selected" ) )
{
hide = false;
break;
}
}
if ( hide ) Datalist.HideDatalists( document.getElementsByTagName( "UL" ) );
}
Datalist.KeyHandler
=
function( evt )
{
evt = evt || window.event;
var isTab = ( 9 == evt.keyCode);
var isEnter = (13 == evt.keyCode);
var isEscape = (27 == evt.keyCode);
var isUp = (38 == evt.keyCode);
var isDown = (40 == evt.keyCode);
if ( isTab )
{
Datalist.HideDatalists( document.getElementsByTagName( "UL" ) );
}
else
if ( isEnter )
{
Datalist.ClickCurrentSelection();
evt.preventDefault();
}
else
if ( isEscape )
{
Datalist.HideDatalists( document.getElementsByTagName( "UL" ) );
}
else
if ( isUp )
{
Datalist.MoveCurrentSelection( -1 );
}
else
if ( isDown )
{
Datalist.MoveCurrentSelection( 1 );
}
}
Datalist.HideDatalists
=
function( elements )
{
var n = elements.length;
for ( var i=0; i < n; i++ )
{
var e = elements[i];
if ( Class.Contains( e, "datalist_list" ) )
{
e.style.display = "none";
e.scrollTop = 0;
Datalist.UnselectItems( e );
}
}
document.removeEventListener( "click", Datalist.OutsideClick );
}
Datalist.UnselectItems
=
function( datalist_list )
{
var elements = datalist_list.getElementsByTagName( "LI" );
var n = elements.length;
for ( var i=0; i < n; i++ )
{
var e = elements[i];
RemoveClass( e, "selected" );
}
}
Datalist.MoveCurrentSelection
=
function( delta )
{
var datalist = Datalist.FindActiveDatalist();
var elements = datalist.getElementsByTagName( "LI" );
var n = elements.length;
var i = -1;
var p = null;
var s = null;
for ( i=0; i < n; i++ )
{
var e = elements[i];
if ( Class.Contains( e, "selected" ) )
{
p = e;
break;
}
}
if ( (-1 == delta) && (i != n) )
{
for ( var j=i-1; j >= 0; j-- )
{
var e = elements[j];
if ( null !== e.offsetParent )
{
s = e;
AddClass ( e, "selected" );
RemoveClass( p, "selected" );
break;
}
}
}
else
if ( 1 == delta )
{
if ( i == n ) i = -1;
for ( var j=i+1; j < n; j++ )
{
var e = elements[j];
if ( null !== e.offsetParent )
{
s = e;
AddClass ( e, "selected" );
RemoveClass( p, "selected" );
break;
}
}
}
if ( s )
{
console.log( "offsetTop: " + s.offsetTop );
if ( (1 == delta) && (s.offsetTop > 439) )
{
s.offsetParent.scrollTop += s.scrollHeight + 1;
}
}
}
Datalist.ClickCurrentSelection
=
function()
{
var datalist = Datalist.FindActiveDatalist();
if ( datalist )
{
var elements = datalist.getElementsByTagName( "LI" );
var n = elements.length;
var i = -1;
for ( i=0; i < n; i++ )
{
var e = elements[i];
if ( Class.Contains( e, "selected" ) )
{
e.click();
break;
}
}
}
}
Datalist.FindActiveDatalist
=
function()
{
var ret = null;
var elements = document.getElementsByTagName( "UL" );
var n = elements.length;
for ( var i=0; i < n; i++ )
{
var e = elements[i];
if ( Class.Contains( e, "datalist_list" ) && (null != e.offsetParent) )
{
ret = e;
break;
}
}
return ret;
}
CSS
.datalist_list
{
display: none;
position: absolute;
z-index: 100;
top: 47px;
box-sizing: border-box;
width: 100%;
max-width: 350px;
max-height: 500px;
overflow-y: auto;
margin: 0;
padding: 0;
list-style-type: none;
border: solid 1px #cccccc;
background: #eee;
}
.datalist_list LI
{
margin: 0;
border-top: solid 1px #cccccc;
padding: 13px 10px;
cursor: pointer;
}
.datalist_list LI.selected
{
background: #3e9afe;
color: white;
}
.datalist_list LI:first-child
{
border: none;
margin: 0;
}