Web2.0 WebPopupMenu keyboard entry?

I can set the tab order for keyboard navigation but how do I allow keyboard entry to select a value?

If you mean the function on Desktop where you can press, for example, “a” while you have a PopupMenu focused and have it select the first item that starts with “a”, then that’s not currently possible.

If you want a searchable PopupMenu for Web, and you’re a current subscriber, GraffitiPopupMenu Web is probably the best alternative.

I’m using 2021r1.1 and when the user pushes the down arrow the menu pops up but doesn’t allow user selection from the keyboard.
So I switch to combo boxes which I will error check to make sure entry matches a row. But when the user tabs, the cursor goes to the end of the text making the user alt+a every box before they can enter.
Maybe I’ve missed a step?

There is a JavaScript command to select the text:


onClick="this.setSelectionRange(0, this.value.length)" 

How do I cause Xojo to execute this in relation to the combo box?

You can put this in the Shown event of a WebComboBox to select all text when it receives focus:

var exec() as String
exec.Add( "var $element = $('#" + me.ControlID + "_input');" )
exec.Add( "$element.focus(function(e) {" )
exec.Add( "  $element.select();" )
exec.Add( "});" )
ExecuteJavaScript( String.FromArray( exec, "" ) )

Thanks Anthony, your code works nicely in the combo box’s text area to select the text.

However my idea of requiring the user to make one of the given choices in the combobox like a menu doesn’t seem possible. There’s no activate event happening before lost focus if the user tries to fix a bad value by selecting from the list so focus is reported lost while the user is still using the control. And the indicators to alert the user of a bad value don’t seem to work (on my Firefox at least) and message boxes are unstable and can also disable the dropdown button. Putting a rectangle behind the combo box and making it visible on an invalid text change looks OK but then the combobox dropdown stops working…

For displaying validation, you can add these two methods to a module:

Public Sub IsValid(extends c as WebCombobox, value as Boolean)
  '// Call as ComboBox1.IsValid = True
  
  if Session = nil then Return
  
  if value then
    Session.ExecuteJavaScript( "$('#" + c.ControlID + "_input').removeClass('is-invalid').addClass('is-valid');" )
  else
    Session.ExecuteJavaScript( "$('#" + c.ControlID + "_input').removeClass('is-valid').addClass('is-invalid');" )
  end if
End Sub
Public Sub ResetValidation(extends c as WebComboBox)
  '// Call as ComboBox1.ResetValidation
  
  if Session = nil then Return
  
  Session.ExecuteJavaScript( "$('#" + c.ControlID + "_input').removeClass('is-valid is-invalid');" )
End Sub

Thanks for this Anthony. Unfortunately the javascript clashes with the combobox. When I pass False the indicator covers up the dropdown arrow so the user can’t see their valid options. And when True' the dropdown is triggered to display the selected value and covers the control beneath it. Ditto Reset`.

If you mean that the icon is overlapping your row value text, then you would need to expand the width of the field to account for that in the default Xojo theme. If you just want to use a different type of indicator rather then the provided Bootstrap form control validation mechanism, then you could switch all that up to something like this, which will change the border color to Danger or Success indicators:

Public Sub IsValid(extends c as WebCombobox, assigns value as Boolean)
  '// Call as ComboBox1.IsValid = True
  
  if Session = nil then Return
  
  if value then
    Session.ExecuteJavaScript( "$('#" + c.ControlID + "_input').removeClass('border-success').addClass('border-danger');" )
  else
    Session.ExecuteJavaScript( "$('#" + c.ControlID + "_input').removeClass('border-danger').addClass('border-success');" )
  end if
End Sub
Public Sub ResetValidation(extends c as WebComboBox)
  '// Call as ComboBox1.ResetValidation
  
  if Session = nil then Return
  
  Session.ExecuteJavaScript( "$('#" + c.ControlID + "_input').removeClass('border-danger border-success');" )
End Sub

Any further customization I’ll leave to you, and you’re welcome to take a look at my ComboBox/PopupMenu alternative, GraffitiPopupMenu, which offers much more functionality.

1 Like

Thanks again for your help with this. I don’t really want to customise. I’m only after keyboard support for the combobox with validation since Web 2 menuing doesn’t work properly.

Unfortunately the border javascript knocked out tabbing between controls. I have also tried putting a small rectangle next to each combobox as an indicator but when changetext event runs it disables the dropdown. In fact it doesn’t seem to fire at all when the user selects from the dropdown.

Lots of Web 2 UX seems broken in 2021r1.1. Will be glad when this project is over. Unfortunately it’s not mine so I can’t buy third party controls for it.

I’m very grateful for your text selection javasript. That works well. I’ll just have to validate all the comboboxes on tab change.

Don’t know if the text selection javscript function caused this, but despite my code with TabPannel’s LastPannelIndex property compiling OK, it doesn’t always work and was AWOL amongst the tab panel’s variables in the debug window…

Here is a solution as best as I know how for 2021r1.11:

In a combo box Shown event (or with the event defined and raised in a super as in this case), per Anthony’s cool JavaScript:

var exec() as String
exec.Add( "var $element = $('#" + me.ControlID + "_input');" )
exec.Add( "$element.focus(function(e) {" )
exec.Add( "  $element.select();" )
exec.Add( "});" )
ExecuteJavaScript( String.FromArray( exec, "" ) )
RaiseEvent Shown

Then in a module:

FindMatch(Extends c as WebComboBox, Indication as WebRectangle) as Boolean
if c.LastRowIndex > -1 then
  dim boolValidText as Boolean
  for i as integer = 0 to c.LastRowIndex
    if c.RowValueAt(i) = c.Text then
      boolValidText = true
      exit for
    end if
  next
  if boolValidText = true then
    Indication.Visible = false
  else
    Indication.Visible = true
  end if
  Indication.UpdateBrowser
  return boolValidText
end if

Then in an event such as a button action etc:

dim boolValidText as Boolean
dim boolAllValidText as Boolean
boolValidText = WebComboBoxBox1.FindMatch(WebRectangle1)
boolAllValidText = boolValidText
boolValidText = WebComboBoxBox2.FindMatch(WebRectangle2)
boolAllValidText = boolAllValidText and boolValidText
//Repeat for the rest of the combo boxes
if not boolAllValidText then
  TabPanel1.SelectedPanelIndex = {{MyPanelIndexNumber}} //returns the user back to the tab to correctly enter the indicated combo boxes
end if

Where the WebRectangle is placed proximate to its corresponding WebComboBox as an error indicator.

The two booleans are neccessary because strangely, boolValidText = boolValidText And WebComboBoxBox2.FindMatch(WebRectangle2) worked correctly as far as the boolean was concered, but casued the .visible = true on the WebRectangle to silenly fail. Separate the bools and all is well… Go figure!

Don’t put that in GotFocus. It hooks into the browser element’s focus event. Put it in Shown event as I instructed. Putting in GotFocus will cause it fire once for each time the control gets focus, first of all, and the first time won’t fire because the focus event will fire, then the event will be sent to the server, then the JavaScript I supplied will be run…which will be too late.

My bad. I fixed it as I had it in my project. Many thanks!

Thought it was solved but it wasn’t. Still ended up with drop down lists covering surrounding controls - what a mess! Is there a way of closing the combo-box lists which fly open with text input?

I’m not sure what you expect to happen. The dropdown appears to show possible values for the user to select, and if there are other controls in the area below the ComboBox where the menu will appear, they will of course be behind that menu. Perhaps you should switch to a WebTextField and populate a list somewhere with possibilities?

You can add this code to the Shown event. It’s not the best solution as the menu will still briefly appear, but it should do what you want.

var exec() as String
exec.Add( "var $element = $('#" + me.ControlID + "_input').keypress(function(e) {" )
exec.Add( "  setTimeout(function() {" )
exec.Add( "    $('#" + me.ControlID + "_menu').removeClass('show');" )
exec.Add( "  }, 1);" )
exec.Add( "});" )
ExecuteJavaScript( String.FromArray( exec, "" ) )

Thanks so much Anthony.
The problem isn’t so much that menus appear but rather I couldn’t make them go away unless the user picks up the mouse for every field defeating the purpose. Because they don’t close on lost focus but just cover where the user wants to go. And they appeared even if there was a match between the list and the manual input.
So I’ll give your JavaScript a try…

Try this in Shown:

var exec() as String
exec.Add( "var $element = $('#" + me.ControlID + "_input').blur(function(e) {" )
exec.Add( "  $('#" + me.ControlID + "_menu').removeClass('show');" )
exec.Add( "});" )
ExecuteJavaScript( String.FromArray( exec, "" ) )
1 Like

Success! The setTimeout seems to be better because it suppresses the flyout lists more. After that the dropdowns don’t work but I discovered Xojo reinitialises the JavaScript Show class your code removed if the following is put into Xojo’s lost focus event:

me.Visible = true
me.UpdateBrowser

Thus dropdowns return to work as usual and your javascript is run again it seems.

Anyway, I’ll go with this and post all the bits together as a solution if there are no further complications…

Thanks again Anthony. Looks like you aced it!