I was looking around for a good HTML combo box (a drop-down list with a type-in text box), and found a good looking one over at Particletree (see Update Your Select Element to a Combo Box). There were a few quirks I found with it.
- The text box used absolute positioning. As a result, if the location of the select element changed (perhaps due to another item on the page being shown or hidden), the text box would be in the wrong place.
- The code was free-standing and applied event handlers directly, instead of using an event manager like one offered in Prototype‘s Event class.
To clean things up, I modified the code to work with the Prototype JavaScript library, converting element selectors and event management. I also added a hack to handle positioning issues by calling a routine that repositions the text box every 0.05 seconds. Though far from efficient, it didn’t cause any flickering or processor utilization. If JavaScript had an “onpositionchanged” event it would be a lot easier.
Either way, below is the modified JavaScript code. Let me know how it works for you, and remember that 99% of it should be attributed to Ryan Campbell of Particletree.
var nTop; var nLeft; var detect = navigator.userAgent.toLowerCase(); function setCombobox(bMethod) { $$('select.comboBox').each(function(combo) { positions = Element.positionedOffset(combo); nTop = positions[1]; nLeft = positions[0]; if(bMethod == true) { inittextfield(combo); //Use iframe hack for Internet Explorer if(!(detect.indexOf("opera") + 1) && (detect.indexOf("msie") + 1)) { initIframe(combo); } } else { textfield = $("txt" + combo.name); textfield.style.top = nTop + "px"; textfield.style.left = nLeft + "px"; if((detect.indexOf("msie") + 1)) { hackFrame = document.getElementById("frame" + combo.name); hackFrame.style.top = nTop + "px"; hackFrame.style.left = nLeft + "px"; } } }); } // ------------------------------------------------------------------ // // Create the textfield and move it to desired position // // ------------------------------------------------------------------ // function inittextfield(ctrl) { selectWidth = ctrl.offsetWidth; //Create textfield textfield = document.createElement("input"); textfield.id = "txt" + ctrl.name; textfield.className = "comboText"; textfield.style.zIndex = "99999"; textfield.value = ""; textfield.style.color = "#ccc"; textfield.style.position = "absolute"; textfield.style.top = nTop + "px"; textfield.style.left = nLeft + "px"; textfield.style.border = "none"; //Account for Browser Interface Differences Here if((detect.indexOf("safari") + 1)) { selectButtonWidth = 18 textfield.style.marginTop = "0px"; textfield.style.marginLeft = "0px"; } else if((detect.indexOf("opera") + 1)) { selectButtonWidth = 27; textfield.style.marginTop = "4px"; textfield.style.marginLeft = "4px"; } else { selectButtonWidth = 27; textfield.style.marginTop = "2px"; textfield.style.marginLeft = "3px"; } textfield.style.width = (selectWidth - selectButtonWidth) + "px"; ctrl.parentNode.appendChild(textfield); ctrl.onchange=function() { val = this.options[this.selectedIndex].value; document.getElementById("txt" + this.name).value = val; } ctrl.onfocus=function() { document.getElementById("txt" + this.name).style.color = "#333"; } textfield.onfocus=function() { this.style.color = "#333"; } } // ------------------------------------------------------------------ // // Internet Explorer hack requires an empty iFrame. We need to add // // one right underneath the div -> it will make the zindex work // // ------------------------------------------------------------------ // function initIframe(ctrl) { textWidth = textfield.offsetWidth; textHeight = textfield.offsetHeight; hackFrame = document.createElement("iframe"); hackFrame.setAttribute("src", "placeHolder.html"); hackFrame.setAttribute("scrolling", "0"); hackFrame.setAttribute("tabindex", "-1"); hackFrame.id = "frame" + ctrl.name; hackFrame.style.position = "absolute"; hackFrame.style.width = textWidth + "px"; hackFrame.style.height = textHeight + "px"; hackFrame.style.top = nTop + "px"; hackFrame.style.left = nLeft + "px"; hackFrame.style.marginTop = "3px"; hackFrame.style.marginLeft = "3px"; ctrl.parentNode.insertBefore(hackFrame, textfield); } Event.observe(window, 'load', function() { setCombobox(true); new PeriodicalExecuter(function(pe) { setCombobox(false); }, 0.05); }); Event.observe(window, 'resize', function() { setCombobox(false); });