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
  • SoftMoon Humane Use License
  • 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 a multi-city intergalactic spacecraft.

    When the Picker interface code was first developed, Microsoft’s Internet Exploder 6 was king and HTML was still in its adolescence.  This concept of a “picker” was fairly new back then; these days the concept is fully embraced and supported by native HTML logistics.  The Picker class now also supports this newer technology and easily integrates with it.  Specifically, ARIA “combobox” and “popup” specifications are incorporated into the Picker’s framework.  For picker pop-ups of role-type “dialog,” integrated Tab key support is also incorporated.  You can download the JavaScript package from our download page or Git it from GitHub: input_type_Picker.

    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 controls” – 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 9 “panels” and 19 “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 JavaScript-applied CSS class-names when “active”, allowing the developer to pop-up and pop-out the picker HTML if desired (as noted ARIA-support is included for pop-ups also), 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>, etc., but for simplicity we will just refer to inputs) has its ARIA attributes modified in real-time to reflect this “pop-up” state of the picker’s panels.  The target <input> 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 as the dataTarget (the object that receives the data picked by the end-user).  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 Color_Picker 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 Color_Picker 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 Color_Picker 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.

    Implementing the Picker class

    The Picker class code resides in the SoftMoon.WebWare namespace to avoid conflict with other software packages.  Back in the day, the code file could create this namespace on-the-fly if need be; then along came the const type of “JavaScript variable”.  The code file can not check for the existance of the namespace and then create it if SoftMoon is a constant, and in the SoftMoon-WebWare world’s codebase, it now is.  So you must first create this namespace: <script> const SoftMoon=Object.defineProperty({}, "WebWare", {value:{}, enumerable:true}); <script> and then load the files: <script scr='JS_toolbucket/+++JS/+++.js'></script> <script scr='JS_toolbucket/SoftMoon-WebWare/UniDOM-2022.js'></script> <script scr='JS_toolbucket/SoftMoon-WebWare/Picker.js'></script>

    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' role='dialog'> <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() { const itybitHTML=document.getElementById('colorPicker'), itybitColorPicker=new SoftMoon.WebWare.Picker(itybitHTML), // no options needed itybitColorInput=document.getElementById('getColor'); function selector(event) { if (event.target.tagName==='TD') itybitColorPicker.pick(event.target.firstChild.data); } UniDOM.addEventHandler(itybitHTML, '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() { const 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.querySelectorAll('#relocatableColorPicker input'); function selector(event) { if (event.target.tagName==='TD') itybitColorPicker.pick(event.target.firstChild.data); } itybitHTML.id+="2"; //we should make the id unique since we cloned it for HTML syntax, but really it doesn’t affect anything UniDOM.addEventHandler(itybitHTML, 'onclick', selector); for (const inp of cInputs) { // ↓↓ note we pass in the <label> for each input as the containing element for the picker. itybitColorPicker.registerTargetElement(inp, inp.parentNode); } }); </script>

    Picker “types”

    With the newer HTML5 spec and the ARIA support specs that go along, there are 5 “types” of picker-interfaces: menu, list, tree, grid, and dialog.  The “dialog” type indicates a complex interface, which the Picker class was designed for, including multiple panels, interface controls, and Tab key control.  But Pickers may also embrace the other “types” which by definition do not typically embrace multiple panels or interface controls, and therefore also have no need for tab-key-control.  ARIA “popup” specs indicate that these other picker-types use a different set of keyboard keys to navigate the “pop-up options” (the arrow keys ( ) and others), and specifically not the Tab key that the Picker class uses to navigate though “interface controls”.  If you implement a picker of type menu, list, tree, or grid it will by definition typically not have interface controls and therefore no integrated tab-key-control.  With these other picker-types, you must develop your own keyboard control within to manage the “required” ARIA keyboard support.  Every interface design will be unique, and the Picker class can not simply adapt, nor do we want to place limits on your interface design.  But the Picker can still manage the “pop-up” and “pop-down” aspects of that for you.  These color-picker examples above would be better suited to being type “list” (instead of the type “dialog” that they are) and as such there should be JavaScript included that allows the end-user to navigate through the list of colors using the arrow keys; but this is just a simple example. 

    As mentioned above, the Picker Class manages more complex situations where the Picker HTML Interface needs to scroll (using a scroll-bar).  Older versions of the Picker for older browsers required any Elements of your picker’s HTML that needed to scroll should be contained within a panel; the panels themselves could not scroll.  I believe this problem is remedied with newer browsers, but it is still unchecked… The idea of the Picker Class is that when you click anywhere on the Picker HTML Interface, “focus” is retained by the “data 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.  The Picker was designed to be intrinsically non-modal and when your “data target” looses focus by the end-user clicking anywhere outside the Picker’s panels or outside of the “data target” or otherwise tabs-out (using the Tab key) of the “data target”, the picker “pops down” (or “pops out”, or “ disappears ”, or otherwise simply becomes “inactive”, depending on your CSS).

    This was the trick in older browsers, because of limitations in the DOM—JavaScript bindings, and the Picker’s code-base was much larger and complex.  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…  With newer HTML5 browsers, there are newer native features that allow JavaScript to “know” more about what is going on.  The latest version of the Picker Class works only with these newer browsers.

    Data Targets

    As mentioned, the “data target” is the JavaScript Object that receives the value that the end-user “picks” (typically a textual value, but there is no global limitation).  The Picker keeps track of the current Data Target using the pickerInstance.dataTarget property.  Typically, the “data target” will be an HTML form element, and specifically <textarea>s, <select>s, and <input>s with or without the list='datalistID' attribute are supported;  but as mentioned, HTML text-nodes are also supported natively.  <input>s may be type='text' or other types typically including color, email, number, password, search, tel, & url<input> types not listed above could also be used, but browsers typically supply a native widget, or they don’t typically interface with humans in a “picker” sort of way; note file-inputs will not allow a picker to change or access the complete file-path in any way, so you can’t use the Picker class with them.  Most “data targets” get their .value property set with the picked-data; <select>s and <input list='datalistID'> have their “options-list” changed.  Data targets may be “additive” or not.  Additive data targets have the “picked data” added to the existing value or options-list; whereas non-additive data targets have their values or an option replaced by the “picked data”. Textual additive data targets may have the “picked data” added where the cursor is placed, or if text is “selected” (highlighted) that section of text will be replaced with the “picked data”. However, if the additive nature is specified as end, the “picked data” will always be added to the end of the existing textual value.  If there is an “options list” with an “additive” element, another “option” will be added to the end of the list (always at the end); if the element is not “additive” the selected option’s old value will be replaced with the new “picked data”.  You can specify that a “data target” is additive (or not — the default anyway) when you register it: pickerInstance.registerTargetElement(input, {additive:'end'}); or by specifying such with an attribute: <input type='text' additive> noting that the additive attribute is Boolean¹ in nature, and indicates true when present without a value; but could also be ='end' or ='false'.  Attribute values override the specs passed in when registering a target, so you would only need ='false' if you were "bulk processing" a bunch of “data targets” and most of them were supposed to be additive.

    Interface Controls

    Your picker(s) themselves may need to adapt to the end-user’s demands, and they do such using “interface controls” – form controls (such as <input>s and <select>s) that modify the “pickers” themselves and/or their behaviors.  The Picker keeps track of the current Interface Control using the pickerInstance.activeInterface property.  Your interface controls may be in (on?) one of your panels or not – that’s up to you and the design specs that work best for you.  If they are in a panel, you need not worry about them, the Picker class takes care of all the details.  If they are free-range (somewhere else in your web-page), you need to register them so the Picker class knows to keep your picker active while the end-user interacts with these “interface controls” (assuming you want to keep it active – that is not strictly required, but usually nice for the end-user).  To do that, use: pickerInstance.registerInterfaceControl(input) noting that input may be any element that accepts keyboard focus (typically, <textarea>, <select>, <button>, as well as <input>, but also note you may customize any element to receive focus using the tabindex attribute).

    Interface Targets

    “Interface Targets” are special “interface controls” 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”.  The Picker keeps track of the current Interface Target using the pickerInstance.interfaceTarget property.  (Note that the pickerInstance.activeInterface property is also set to this element.)  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 receive 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.  An “interface control” in your panel may be designated as being an “interface target” by giving it the HTML attribute interfaceTarget='true' (note this is a “Boolean¹” attribute, and its presence without a value equates to "true"); or you can register it using pickerInstance.registerInterfaceControl(input, {interfaceTarget:true}); (if it is on a picker-panel, register it before you register the panel).  Or when you register a picker-panel, you may specify that all interface-controls within are interface-targets using pickerInstance.registerInterfacePanel(panel, {interfaceTarget:true}); and in this case, individual <input>s (etc.) may have the attribute interfaceTarget='false'.

    .currentTarget & .currentInterface

    These properties of a pickerInstance are “convenience” properties.  The .currentTarget will return either the .interfaceTarget or the .dataTarget, whichever is currently active.  The .currentInterface will return either the .activeInterface or the .dataTarget, whichever is currently active.  Note that .currentTarget & .currentInterface are “getters” only; you can not “set” thier values, as you can the .dataTarget, .activeInterface, & .interfaceTarget properties.

    Multiple “panels” & “pickers” with “dialog” type pickers

    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 and not within a registered “panel”, assuming you want to keep the “focus” of the target <input> while selecting different pickers. 

    ARIA support with multiple “panels”

    If you do choose to implement more than one picker-panel, you need to inform your Picker instance on how to handle the ARIA-support for pop-ups (unless for some reason you don’t want ARIA supported).  When your picker is active, the “target input” is told what element “pops-up” and this can then be made properly available to a screen reader, etc.  Typically, it is nice to have all the panels nested within this one containing “pop-up” element, and this element should contain nothing else; then when you create a new Picker instance, pass in that containing element as the value for opts.aria_popUp.  The MasterColorPicker project uses that approach.  This is not always possible based on the HTML layout you need to implement.  When multiple picker-panels can not be nested within a single container, the specified “aria-pop-up container” should use the HTML attribute aria-owns to point to the free-range picker-panel(s), so the screen-reader can properly find and acknowledge them.  Alternatively, your “main-panel” may use aria-owns to point to the other “auxiliary” panels, as does the example below.  If you only have the one single main-panel and you don’t pass in a value for opts.aria_popUp then the main-panel automatically becomes the “popup dialog element”, as in the mini-color-picker example above.  If you don’t want the Picker to support ARIA, simply pass in opts.aria_popUp=null when you create your Picker instance.  At any time after you create a picker, you can also modify pickerInstance.aria_popUp to whatever value you desire.  See more about ARIA below.

    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' role='combobox' aria-haspopup='dialog'></label> <label>2. <input type='text' role='combobox' aria-haspopup='dialog'></label> <label>3. <input type='text' role='combobox' aria-haspopup='dialog'></label> <div id='songPickerHTML' role='dialog' aria-owns='songPickerControls'><!-- 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' role='dialog'> <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

    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.

    ARIA support logistics

    Your picker-panel(s) do not need to “pop-up” and “pop-down”.  It/they may always be displayed to the user, if that is what you choose or need.  In that case, as mentioned above, pass in opts.aria_popUp=null to the Picker constructor, or set pickerInstance.aria_popUp=null after you construct your picker.  If you do want you picker-panel(s) to “pop-up” when the associated <input> (etc.) is active (has keyboard focus), as described above, you need to tell your Picker instance what the HTML pop-up element is by setting the Picker’s .aria_popUp value.  From there, the Picker class takes care of the dirty details.  The Picker supports basic rules of the HTML element “combobox-role”.  Your pickerInstance.aria_popUp element (remember, which may automatically be your mainPanel if it is the only one) should by definition have a role attribute that describes what kind of “pop-up” it is: menu, list, tree, grid, or dialog.  The picker recognizes this when your Picker is active, and sets the dataTarget <input>’s aria-haspopup attribute properly according to the role attribute of your “pop-up” (note: remember, other HTML dataTarget elements are also supported besides <input>). It also properly sets the dataTarget’s aria-controls and aria-expanded attributes when your Picker is active; as such, your pickerInstance.aria_popUp element should (by definition) have an id attribute.  When your Picker becomes inactive, it removes these attributes from the dataTarget, allowing another <input> (etc.) to become the dataTarget for your Picker. It is up to you to supply any JavaScript code to make the internals of your picker-panels work according to ARIA rules for menus, lists, trees, or grids.  For Pickers with the dialog role, this class supports the Tab key natively for your “interface controls” that reside on the picker-panel(s), and which integrates with the native “multi-panel” support by necessity:

    The Tab key with “dialog” type Pickers

    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"; these are “Boolean¹” attributes, and as such their simple inclusion without a value indicates a “true” value.  When registering an “interface panel” the first and last “interface controls” 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 separately 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 developer wants, or with any other plugin?  Since registering an “interface control” interferes with the tab-key control by necessity, I thought it best to fully integrate tab-key direction within this project for Version 2+, and enhanced that in Versions 5+.  This allows so much more flexibility and dynamicity in developing your Picker, with multiple panels that may be added/removed and active/inactive 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 controls”: “tabTo” and “backTabTo”.  They may have one of three value types.  Their values may be the HTML “id” of the Element to tab to, by preceding the “id” with a # symbol similar to CSS specs.  For example: <input tabTo='#myWebPageElement'>.  The @ symbol may be used to signify one of several “special cases” that can not be simply pointed to or scripted.  These are @next-panel, @previous-panel, @adjacent-panel, and @target.  (see below for more about tabbing from one panel to another)  For example: <input tabTo='@next-panel'>.  The @adjacent-panel value relies on the Shift key being pressed to determine if the panel is the “next” or “previous” one.  If you only have two (2) panels, use this value.  The @target value works like the tabToTarget and backTabToTarget attributes noted above, but it can not signify a false value like they can.  The value of a “tabTo” or “backtabTo” attribute may also be a JavaScript expression that evaluates to the Element to tab to.  The value of this within the expression is the “interface control” element that has keyboard focus; you also have access within your expression to the values of the keydown event, the PickerInstance, and the actions Object that you passed in when you registered the interface control.  For example: <input tabTo='this.closest(".panel").querySelector(
       (event.shiftKey || actions.forceShiftKey) ^ PickerInstance.toggleShiftKeyAction ? ".coldButton" : ".hotButton")'>

    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 control” 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 control” itself may have an attribute keep-focus and this may have a Boolean¹ value that overrides the pickerInstance default.  For HTML <button> “interface controls” the Picker also adds a customized buttonpress event when the <button> has keyboard focus and the user presses the ↵Enter key.

    notes

    1. Boolean attribute values
    The Picker uses the (nonstandard) Boolean.eval() function found in the JS_toolbucket/+++JS/+++.js file.  It recognizes more than simply "true" & "false".  You can use any of the values it recognizes, including "yes" & "no".