I would like to be able to create a form with datalist dependent on other datalist for provinces and localities for example. I have found a good datalist compatible with all browsers included safari which is not supported natively. I hope you can help me, this is the code:
html:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8" />
<title>Pure JS datalist polyfill</title>
<link rel="stylesheet" href="css/datalist.css"/>
<body>
<div class="main">
<input id="input" type="text" list="states" placeholder="Elige provincia">
<datalist id="states">
<option value="Alabama">
<option value="Alaska">
<option value="Arizona">
<option value="Arkansas">
<option value="California">
<option value="Colorado">
<option value="Connecticut">
<option value="Delaware">
<option value="Florida">
<option value="Georgia">
<option value="Hawaii">
<option value="Idaho">
<option value="Illinois">
<option value="Indiana">
<option value="Iowa">
<option value="Kansas">
<option value="Kentucky">
<option value="Louisiana">
<option value="Maine">
<option value="Maryland">
<option value="Massachusetts">
<option value="Michigan">
<option value="Minnesota">
<option value="Mississippi">
<option value="Missouri">
<option value="Montana">
<option value="Nebraska">
<option value="Nevada">
<option value="New Hampshire">
<option value="New Jersey">
<option value="New Mexico">
<option value="New York">
<option value="North Carolina">
<option value="North Dakota">
<option value="Ohio">
<option value="Oklahoma">
<option value="Oregon">
<option value="Pennsylvania">
<option value="Rhode Island">
<option value="South Carolina">
<option value="South Dakota">
<option value="Tennessee">
<option value="Texas">
<option value="Utah">
<option value="Vermont">
<option value="Virginia">
<option value="Washington">
<option value="West Virginia">
<option value="Wisconsin">
<option value="Wyoming">
</datalist>
</div>
<script>
(function() {
var inputs = document.getElementsByTagName('input');
for( var i = 0; i < inputs.length; i++ ) {
var input = inputs[i];
input.onchange = function(evt) {
var elem = evt ? evt.target : window.event.srcElement;
// alert('new value: ' + elem.value);
};
}
}());
</script>
<script src="js/datalist.js"></script>
</body>
</html>
css to see more or less well:
#input {
width: 300px;
height: 44px;
font-size: 16px;
padding: 10px;
position: absolute;
top: 100px;
left: 100px;
border-radius-ourline: 15px;
}
.datalist-polyfill {
list-style: none;
display: none;
background: white;
box-shadow: 0 2px 2px #999;
position: absolute;
left: 0;
top: 0;
margin: 0;
padding: 0;
max-height: 300px;
overflow-y: auto;
}
.datalist-polyfill:empty {
display: none !important;
}
.datalist-polyfill > li {
padding: 3px;
font: 13px "Lucida Grande", Sans-Serif;
}
.datalist-polyfill__active {
background: #3875d7;
color: white;
}
js:
(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));