Custom Web Software Development

JavaScript, HTML 5, CSS 4, & PHP 8: Innovative Enterprise level Scripting for interactive sites, SaaS, & cross-platform desktop apps
  • Home
  • Automotive Dealer Website Development
  • Free desktop apps
  • Rainbow-Maker
  • MasterColorPicker
  • Free libraries: JavaScript and PHP
  • <input type="picker">
  • RGB_Calc color-space converter
  • FormFieldGenie fieldset multiplier
  • UniDOM.js x-browser solution +
  • eMail address validation scripts
  • PHP Utility Functions
  • ActiveState Komodo Edit support
  • JavaScript powered <input type="picker">

    HTML forms are an essential part of interacting with web-page viewers, and the <input> tag is the most common interface tool used.  However, it gives little help to users that need to enter complex and/or specific data.  HTML5 introduces the <datalist> element which can turn an <input> tag into a hybrid with a <select> tag, giving the user a pull-down menu to choose from without limiting the input data to only the menu items.  But sometimes that’s not enough…you want to present to your users a complex table of data to choose from, or maybe some other source needs to generate the data on-the-fly, such as a color-picker (like the MasterColorPicker project, for which this project was developed).  So here I present the <input type="picker"> project.  If an <input type="text"> tag is like a car, and a <textarea> tag is like a pickup truck, and a <select> tag is like a bus, and an <input type="text"> with a <datalist> is like a train, then an <input> with a Picker interface is like an multi-city intergalactic spacecraft.

    Using the Picker Class with an <input type="text"> is simple.  Just as you would do using the <datalist> tag, the “picker” HTML is separate from the <input> tag itself.  However, <datalist>s are simple “lists”, and you may only associate one <datalist> with each <input>.  With the Picker Class you may associate any number of “panels” (HTML elements), and each panel may have any number of “pickers” along with any other HTML markup and auxiliary displays and data, as well as “interface” elements − form controls (such as <input>s and <select>s) that modify the “pickers” themselves and/or their behaviors.  Furthermore, not only may you associate multiple “panels” and “pickers” with an <input>, you may associate multiple <input>s with the same “panel”(s)/“picker”(s).  For instance, the MasterColorPicker project uses 8 “panels” and 14 “pickers” by default (at this time of writing), and the Rainbow-Maker 2 project associates the MasterColorPicker with many different <input>s.

    “Panels” and their “pickers” receive applied CSS class-names when “active”, allowing the developer to pop-up and pop-out the picker HTML if desired, or style it any way needed for complete flexibility.  “Panels” may be styled any way you choose using your custom CSS, and if you have more than one panel, they may visually overlap on the display;  the Picker Class manages an additional set of class-names which allows you to manage the Z-index level of each panel, so the last one clicked on may be “brought to the top.”  The target <input> (or <textarea> but for simplicity we will just refer to inputs) also needs to be kept in focus when you click on the Picker Interface “panel(s).”  Additionally, “panels” and “pickers” may be assigned user-defined event-handlers which are triggered when they become “active” or “inactive” (onPickerStateChange and onInterfaceStateChange).  All this is managed by the Picker Class by a complex interaction − a dance − of event-handlers.  This is its primary function in life.  It would be easy to have a JavaScript function that simply adds/replaces an <input>’s value with anything when you click on whatever…  And it’s easy to keep up with which <input> is the one of many of which to replace the old value with a new value.  The trick is keeping up with when an <input> is “active”, especially when Picker-interfaces need to scroll or if they need to use another HTML element that requires keyboard “focus.”  Generally, when an <input> is associated with a Picker-interface, the <input> and Picker are considered “active” when the <input> has “focus.”  If your interface itself needs to have an HTML element that requires keyboard “focus”, including other <input type='text'>s, <input type='file'>s, <input type='number'>s, <textarea>s, <select>s, etc., then they need to be “registered.”  The Picker Class gives you all you need to manage all the event-handlers required to maintain the “focus” of the target <input> associated with a Picker-interface when necessary, and to keep track of when the Picker-interface is “active”, and when it is not.  The comments in the code file explain all this. 

    But wait!  You don’t even need to use an <input type='text'> with the Picker Class.  HTML <textarea>s, <select>s, <input type='text' list='datalistID'>s, and even document text-nodes are automatically recognized by the Picker_instance.pick() method.  By default, the Picker_instance.dataTarget receives the data-value picked by the end user.  If no dataTarget is set then the Picker_instance.masterTarget is used, if any.  The dataTarget and masterTarget each may be any JavaScript Object.  Or they may be <input type='hidden'> if you don't care to show the user the picked value, but want to send the value back to the server with a form-submittal.  To supplement this functionality, add a JavaScript function to the Array of Picker_instance.pickFilters.  To replace this functionality, replace the Picker_instance.pick method with your own function.

    There is a plethora of comments embedded in the Picker JavaScript code which you should read if you want to use this package.  Beyond that, as usual a simple example does best to explain the usage of the Picker Class Object.  Below you will see the code for a simple HTML Picker example and the working demo of this example code.  The MasterColorPicker project is a more complex example of using the Picker package.  If you study the MasterColorPicker2.js file, you will see the x_ColorPicker Class (found near the beginning of that file) acts as an intermediary between the color-pickers and the Picker Class.  At the end of each color-picker’s code section you will see the event-handlers attached to each color-picker, and how they interact with the x_ColorPicker class to “pick” colors;  and the YinYang NíHóng Color Picker code shows an advanced example of using the onPickerStateChange event.  At the end of the MasterColorPicker2.js file you will see how the MasterColorPicker Object (which is used by the x-ColorPicker Class) is created from an instance of the Picker Class, an example of how the pickFilter works, and another example of using the onPickerStateChange event.

    All these examples below use the following simple CSS: <style> label { display: inline-block; position: relative; margin-bottom: 4em; } label .pickerPanel { position: absolute; top: 1.1618em; right: -10.618em; } .pickerPanel {display: none;} .activePicker {display: table;} </style> A very simple example: <label>Pick a color <input type='text' id='getColor' /> <table id='colorPicker'> <caption>Colors</caption> <tr> <td style='background-color:red'>red</td> <td style='background-color:orange'>orange</td> <td style='background-color:yellow'>yellow</td> <td style='background-color:green'>green</td> <td style='background-color:cyan'>cyan</td> <td style='background-color:blue'>blue</td> <td style='background-color:violet'>violet</td> <td style='background-color:indigo'>indigo</td> </tr> </table></label> <script type='text/javascript'> // note how you can use the required/included “UniDOM” package to add event handlers, or add them any other way. UniDOM.addEventHandler(window, 'onload', function() { var itybitHTML=document.getElementById('colorPicker'), itybitColorPicker=new SoftMoon.WebWare.Picker(itybitHTML), // no options needed itybitColorInput=document.getElementById('getColor'), itybitColors=itybitHTML.getElementsByTagName('td'), selector=function() {itybitColorPicker.pick(this.firstChild.data);}; for (var i=0; i<itybitColors.length; i++) { UniDOM.addEventHandler(itybitColors[i], 'onclick', selector); } itybitColorPicker.registerTargetElement(itybitColorInput); }); </script>
    Basically the same, but with two different target-inputs, and a relocating Picker.  Note the difference in the way the dataTarget <input>s are registered: <div id='relocatableColorPicker'> <label>Foreground Color:<input type='text' /></label> <label>Background Color:<input type='text' /></label> </div> <script type='text/javascript'> UniDOM.addEventHandler(window, 'onload', function() { var itybitHTML=document.getElementById('colorPicker').cloneNode(true), // we might instead use “removeNode” but we keep the original because it is used in the previous example. itybitColorPicker=new SoftMoon.WebWare.Picker(itybitHTML), // no options needed cInputs=document.getElementById('relocatableColorPicker').getElementsByTagName('input'), itybitColors=itybitHTML.getElementsByTagName('td'), selector=function() {itybitColorPicker.pick(this.firstChild.data);}; for (var i=0; i<itybitColors.length; i++) { UniDOM.addEventHandler(itybitColors[i], 'onclick', selector); } for (var i=0; i<cInputs.length; i++) { // ↓↓note we pass in the <label> for each input as the containing element for the picker. itybitColorPicker.registerTargetElement(cInputs[i], cInputs[i].parentNode); } }); </script>

    As mentioned above, the Picker Class manages more complex situations where the Picker HTML Interface needs to scroll (using a scroll-bar).  The idea of the Picker Class is that when you click anywhere on the Picker HTML Interface, “focus” is retained by the target <input>, even if nothing is “picked.”  Click on the caption of the itybitColorPicker above (where it says “Colors”), or anywhere on the yellow background of the song-picker below, as a demonstration of this principle.

    This was the trick in older browsers, because often times clicking on a scroll-bar did not generate an onclick event.  Focus is taken away from the target <input>, and it needs to be “refocused.”  The Picker Class needs to keep track of where the mouse is, so when a scroll-bar is clicked on this situation can be remedied.  Sometimes with older browsers the “onclick” event for the Picker HTML comes before the “onblur” event for the <input>, sometimes the other way around.  If the “blur” occurs first, depending on your CSS rules of course, the Picker HTML may be hidden before the “click” happens; then the click does not register on the Picker, but rather on whatever is underneath.  The older version of Picker Class remedies this by keeping track of when the mouse is hovering over a “Picker panel,” and also when it is hovering over the first-of-kin children.  If it is over the “panel,” but not over any of the children, then it must be over the scroll-bar.  If a registered target <input> looses focus, it will automatically refocus when the mouse is hovering over said scroll-bars.  At this point it might be thought “why not simply refocus if the mouse is over the Picker panel in general?”  That then voids the “onclick” event; it simply then never occurs and nothing gets “picked” — at least in some browsers under some conditions;  I pulled hair for a while when the original prototypes for the Picker code would work and then wouldn’t, so I would modify the way the handlers interacted, and that would work and then not…  Anyway, the “onclick” event on a picker-panel then refocuses the dataTarget <input>.  That was all a mess that still sometimes failed in complex situations.

    With newer HTML5 browsers, multiple events and event-handlers have more of a “defined” order of occurance, based (at least) on the order that handlers are registered.  The “dance of event-handlers” is by definition random in the old DOM standard.  The latest version of the Picker Class works only with the newer browsers, and without needing to keep track of the mouse.

    If you want to use more that one “panel” (i.e. more than the mainPanel, which is passed in as the first (required) argument when creating a new Picker instance), you need to “register” the additional panels, the same as you need to “register” interface control-elements (see above, and also the comments in the Picker code file).  Registering an additional panel (besides the mainPanel) also automatically registers any “interface control-elements” within that panel.  If you want to use more than one “picker,” the Picker Class can manage that also, provided all the “pickers” reside within these “registered” PickerInstance.panels (including the mainPanel).  The options Object that can be passed in as a second argument when creating a new Picker instance may hold a pickerSelect property that references either a <select> tag or an array of <input type='checkbox‖radio'> buttons that can control which “Pickers” are considered “active.”  You should also probably “register” your pickerSelect if it is a <select> tag, assuming you want to keep the “focus” of the target <input> while selecting different pickers. 

    One final example (using the same CSS as those above, but with the addition shown below): <style> #songs .picker { display: none; } #songs .activePicker { display: table; } #songs { padding: 2em 0 .618em 0; margin: .618em 12.618em; position: relative; } #songPickerControls.activePicker { position: absolute; top: 1.1618em; left: 0; display: block; width: 100%; text-align: center; background-color: yellow; } #songs label { margin: 0 .618em; } #songPickerHTML { background-color: yellow; border: 2px solid blue; } </style> <fieldset id='songs'><legend>Pick 3 songs</legend> <label>1. <input type='text' /></label> <label>2. <input type='text' /></label> <label>3. <input type='text' /></label> <div id='songPickerHTML'><!-- this is the picker’s mainPanel --> <table id='PinkFloyd' class='picker rock'><caption>Pink Floyd</caption> <tr><th>song</th><th>from album</th></tr> <tr><td>Set the Controls for the Heart of the Sun</td><td>A Saucerful Of Secrets</td></tr> <tr><td>Main Theme</td><td>More</td></tr> <tr><td>Echoes</td><td>Meddle</td></tr> </table> <table id='GratefulDead' class='picker rock'><caption>Grateful Dead</caption> <tr><th>song</th><th>from album</th></tr> <tr><td>Bertha</td><td>Grateful Dead</td></tr> <tr><td>China Cat Sunflower</td><td>Aoxomoxoa</td></tr> <tr><td>Eyes Of The World</td><td>Wake Of The Flood</td></tr> </table> <table id='LedZeppelin' class='picker rock'><caption>Led Zeppelin</caption> <tr><th>song</th><th>from album</th></tr> <tr><td>Whole Lotta Love</td><td>Led Zeppelin II</td></tr> <tr><td>Stairway to Heaven</td><td>Led Zeppelin IV</td></tr> <tr><td>In The Light</td><td>Physical Graffiti</td></tr> </table> <table id='MilesDavis' class='picker jazz'><caption>Miles Davis</caption> <tr><th>song</th><th>from album</th></tr> <tr><td>Flamenco Sketches</td><td>Kind Of Blue</td></tr> <tr><td>“Shhh”/“Peaceful”</td><td>In A Silent Way</td></tr> <tr><td>Pharaoh’s Dance</td><td>Bitches Brew</td></tr> </table> <table id='TheloniusMonk' class='picker jazz'><caption>Thelonius Monk</caption> <tr><th>song</th><th>from album</th></tr> <tr><td>Bright Mississippi</td><td>Monk’s Dream</td></tr> <tr><td>I’m Confessin’ (That I Love You)</td><td>Solo Monk</td></tr> <tr><td>Ugly Beauty</td><td>Underground</td></tr> </table> <table id='TedeschiTrucksBand' class='picker blues'><caption>Tedeschi Trucks Band</caption> <tr><th>song</th><th>from album</th></tr> <tr><td>Don’t Let Me Slide</td><td>Revelator</td></tr> <tr><td>Love Has Something Else To Say</td><td>Revelator</td></tr> <tr><td>Learn How To Love</td><td>Everybody’s Talkin’</td></tr> </table> <table id='PinkAnderson' class='picker blues'><caption>Pink Anderson</caption> <tr><th>song</th><th>from album</th></tr> <tr><td>Papa’s About To Get Mad / Gonna Tip Out Tonight</td><td>(single)</td></tr> <tr><td>Every Day In The Week Blues / C.C And O. Blues</td><td>(single)</td></tr> <tr><td>Greasy Greens</td><td>American Street Songs</td></tr> </table> <table id='FloydCouncil' class='picker blues'><caption>Floyd Council</caption> <tr><th>song</th><th>from album</th></tr> <tr><td>I Don’t Want No Hungry Woman</td><td>Carolina Blues</td></tr> <tr><td>Lookin’ For My Baby</td><td>Carolina Blues</td></tr> <tr><td>Workin Man Blues</td><td>Carolina Blues</td></tr> </table> </div><!-- close songPickerHTML --> <div id='songPickerControls'> <label>Style: <select id='musicStyle'> <option selected='selected'>rock</option> <option>jazz</option> <option>blues</option> </select></label> <label>Artist: <select id='artists'> <option class='rock' selected='selected'>Pink Floyd</option> <option class='rock'>Grateful Dead</option> <option class='rock'>Led Zeppelin</option> <option class='jazz'>Miles Davis</option> <option class='jazz'>Thelonius Monk</option> <option class='blues'>Tedeschi Trucks Band</option> <option class='blues'>Pink Anderson</option> <option class='blues'>Floyd Council</option> </select></label> </div> </fieldset> <script type='text/javascript'> UniDOM.addEventHandler(window, 'onload', function() { var songsHTML=document.getElementById('songPickerHTML'), musicStyle=document.getElementById('musicStyle'), artistSelect=document.getElementById('artists'), sInputs=document.getElementById('songs').getElementsByTagName('input'), songs=songsHTML.getElementsByTagName('tr'), SongPicker=new SoftMoon.WebWare.Picker(songsHTML, {picker_select: artistSelect), selector=function() {SongPicker.pick(this.firstChild.data);}, setStyle=function(onBoot) { for (var i=0, flag=false; i<artistSelect.options.length; i++) { artistSelect.options[i].disabled=!(this.value===artistSelect.options[i].className); if (!flag) artistSelect.options[i].selected=(flag=(this.value===artistSelect.options[i].className)); } if (onBoot!==true) SongPicker.choosePicker(); }; for (var i=0; i<songs.length; i++) { //attach event handlers to each song in every table if (songs[i].firstChild.nodeName==='TD') UniDOM.addEventHandler(songs[i].firstChild, 'onclick', selector); } for (i=0; i<sInputs.length; i++) { // ↓↓note we pass in the <label> for each input as the containing element for the picker. SongPicker.registerTargetElement(sInputs[i], sInputs[i].parentNode); } SongPicker.registerInterfacePanel(document.getElementById('songPickerControls')); UniDOM.addEventHandler(musicStyle, 'onchange', setStyle); setStyle.call(musicStyle, true); }); </script>
    Pick 3 songs
    Pink Floyd
    songfrom album
    Set the Controls for the Heart of the SunA Saucerful Of Secrets
    Main ThemeMore
    EchoesMeddle
    Grateful Dead
    songfrom album
    BerthaGrateful Dead
    China Cat SunflowerAoxomoxoa
    Eyes Of The WorldWake Of The Flood
    Led Zeppelin
    songfrom album
    Whole Lotta LoveLed Zeppelin II
    Stairway to HeavenLed Zeppelin IV
    In The LightPhysical Graffiti
    Miles Davis
    songfrom album
    Flamenco SketchesKind Of Blue
    “Shhh”/“Peaceful”In A Silent Way
    Pharaoh’s DanceBitches Brew
    Thelonius Monk
    songfrom album
    Bright MississippiMonk’s Dream
    I’m Confessin’ (That I Love You)Solo Monk
    Ugly BeautyUnderground
    Tedeschi Trucks Band
    songfrom album
    Don’t Let Me SlideRevelator
    Love Has Something Else To SayRevelator
    Learn How To LoveEverybody’s Talkin’
    Pink Anderson
    songfrom album
    Papa’s About To Get Mad / Gonna Tip Out Tonight(single)
    Every Day In The Week Blues / C.C And O. Blues(single)
    Greasy GreensAmerican Street Songs
    Floyd Council
    songfrom album
    I Don’t Want No Hungry WomanCarolina Blues
    Lookin’ For My BabyCarolina Blues
    Workin Man BluesCarolina Blues

    The above example need not use two panels, and realistically complicates a simple interface.  But if you were to include items such as dates, publishers, playtimes, etc. into the tables in this example, you may want to filter the possible song selections using any number of factors.  In a real-world shopping-cart project, having these filters in a separate panel from the “pickers,” maybe even one that is always shown to the end-user, can be an optimal way of organizing your HTML page.

    The version 2+ release of the Picker Class adds even better support for “interface Elements” in the pickers you develop.  Tab-key support within interface Elements has been expanded, and a new concept of “interface Targets” is now available for use.  With version 2+ of the Picker Class, the non-standard HTML attributes “tabToTarget” (same as Version 1 release) and “backTabToTarget” may have values of "true" or "false".  When registering an “interface panel” the first and last “interface Elements” found on the panel by default are registered to backtab-to-target and tab-to-target, respectively.  You can override this with the HTML attributes markup, or with JavaScript by “registering” them seperately with the desired options before registering the panel they are on (see the comments in the code-file).

    For more complex tab-control needs, HTML of course gives you the tabIndex attribute, but this can be confusing at best sometimes.  If you are creating a plugin that should work within any web-page, how can you be sure that your tab-indexes are not going to conflict with what the develper wants, or with any other plugin?  Since registering an “interface Element” interferes with the tab-key control by nessesity, I thought it best to fully integrate tab-key direction within this project for Version 2.  This allows so much more flexibility and dynamicy in developing your Picker, with multiple panels that may be added/removed according to needs (perhaps loaded via Ajax on demand).  So two more additional HTML attributes are now recognized by the Picker Class for “registered interface Elements”: “tabTo” and “backTabTo”.  Their values may be either an HTML “id” of the Element to tab to, or a JavaScript expression that evaluates to the Element to tab to.

    Users may also want to manually tab from one panel to another.  By default they could now do this using CTRL+Tab and SHIFT+CTRL+Tab except for that most browsers block these key-combos from JavaScript.  So by default the Picker class also recognizes CTRL+< and CTRL+> (on QUERTY keyboards this would actually be SHIFT+CTRL+,< and SHIFT+CTRL+.>) to serve this need.  On non-QUERTY keyboards, the < and > keys may normally generate characters when pressed in combo with the CTRL key, and the Picker’s default panel-tab control may then conflict (and not work!).  You may alter the values found in: pickerInstance.panelTabKey and pickerInstance.panelBacktabKey which are UniDOM.KeySniffer objects.  A value of true or false for a modifier-key means that key must be in the indicated state.  A value of undefined for a modifier-key means the KeySniffer doesn’t care.

    You may want your “interface Element” to remain focused when it has focus when the user presses the ↵Enter key; or maybe you want keyboard focus to return to your data-target (<input>).  The Boolean value found at pickerInstance.doKeepInterfaceFocus controls that default action; but your “interface Element” itself may have an attribute keep-focus and this may have a Boolean value that overrides the pickerInstance default.  For HTML <button> “interface Elements” the Picker also adds a customized buttonpress event when the <button> has keyboard focus and the user presses the ↵Enter key.

    “Interface Targets” are special “interface Elements” of your Picker that can receive a value “picked” from another part of your Picker, where normally a user “picks” something for the “data Target”.  For instance, the MasterColorPicker Version 2 project has a “color filter” that filters the color a user “picks” from one of the palettes, modifying the “picked” color by mixing in the “filter-color”.  The user must first choose this filter-color, by picking a color from one of the palettes.  So the color-filter panel has an “interface Target” to recieve the filter-color picked.  As usual, the Picker Class makes sure keyboard focus is directed to the proper Element at all times, as well as properly managing the “top panel” under these shifting conditions.