Set focus on programmatically created buttons

What I’m trying to do:

I have this form, PasswordView, that is activated for users to fill up. The form does not have normal physical buttons. Instead, they are created programmatically as below.

    done = False
    count = 0
    while not done:
    # get user id a separate window
      self.clear()
      search_id = {}
      alert(
        content= PasswordView(item = search_id),
        title="CONFIRM OPEN FORM WITH USER ID",
        large=True,
        buttons=[("Submit", True), ("Cancel", False)]
      )

…followed by more codes

How can I set focus on ‘Submit’ so that users may just press ‘Enter’ instead of pointing the cursor and click?

What I’ve tried and what’s not working:

I kind of just guessed wildly, if I may, using the ‘self’ thingy but I know it won’t work since there are no instantiated objects. So, those were just wasted minutes.

Would greatly appreciate any tips on this issue.
Much thanks.

Clone link:
share a copy of your app

Right now I’m on my phone and I don’t remember how to do that.

But I remember that that was one of the problems I addressed with the InputBox custom component: Input_box() and alert2()

You can have a look at the code to see how I addressed it, or you can use the custom component, which manages the focus for you.

One thing you can do, is to add normal buttons, that you can focus, to your form PasswordView with click events:

def submit_btn_click(self, **eventargs):
   self.raise_event('x-close-alert', value=True)

def cancel_btn_click(self, **eventargs):
   self.raise_event('x-close-alert', value=False)

and then change your alert to:

alert(
        content= PasswordView(item = search_id),
        title="CONFIRM OPEN FORM WITH USER ID",
        large=True,
        buttons=[]
      )

I have used your Input_box and Alert2() successfully but did not think to look there. I will check on it for sure. Much thanks.

Thank you.

I was actually going to do that next but thought I might just check if it’s possible without the normal button object.

I think the reason it can’t with non form content is that the alert is purely synchronous and so any code the calling form runs can’t affect the internals of the alert after it launches (like for finding the elements of the built in buttons with CSS Selectors) for that we’d need an async version I think.

Hello @stein,

I tried the normal button approach and it’s OK.

However, I had to add the below method to make the button ‘Submit’ work when pressing Enter.

  def text_box_password_pressed_enter(self, **event_args):
    self.outlined_button_submit_click()

Though it works for me, it is not actually setting the focus on the “Submit” button as I originally wanted.

Thanks.

You’ll need to add some custom code to focus the button

add a form show event to your alert component in the designer and add the following code

from anvil.js import get_dom_node

...


    def form_show(self, **event_args):
        btn = get_dom_node(self.submit_button).querySelector('button')
        btn.focus()



here’s a clone link to demonstrate

2 Likes

Thank you, @stucork. Will do it,
Another great lesson from you … greatly appreciated.

1 Like

Hello @stucork,
I tested your codes to focus on a physical button suggested by @stein. These are the codes:

## On PasswordView form

from anvil.js import get_dom_node

def text_box_password_pressed_enter(self, **event_args):
    #self.submit_button_click()
     pass 

def form_show(self, **event_args):
    btn = get_dom_node(self.submit_button).querySelector('Submit')
    btn.focus()

def submit_button_click(self, **event_args):
    self.raise_event('x-close-alert', value=True)

def cancel_button_click(self, **event_args):
    self.raise_event('x-close-alert', value=False)

I got this error:

AttributeError: 'NoneType' object has no attribute 'focus'. It seems the "Submit’ button could not be found.

Would appreciate your correction.

Thank you.

that’s not the code i wrote

    def form_show(self, **event_args):
        btn = get_dom_node(self.submit_button).querySelector('button')
        btn.focus()

you’ve changed the querySelector

querySelector selects the html button element
You’ve replace "button" with "Submit"
There is no such thing as an html "Submit" element

If you revert your change to the code I wrote it should work.

OK, my bad.
Let me restore that line to yours and test it again.
I will give an update here,
Thank you.

So, @stucork, here’s the outcome from restoring your code:

def outlined_card_password_show(self, **event_args):
    btn = get_dom_node(self.submit_button).querySelector('button')
    btn.focus()

I can confirm that your code works…it puts the focus on the submit_button in my PasswordView form. The only issue is that the focus is temporary. Once I start entering text in the text_box for password, the focus on the button is lost.

Anyway to keep that focus steady? Possible to insert your code inside def text_box_password_pressed_enter?

Update on this issue:

I tried to insert @stucork codes inside the def text_box_password_pressed_enter and it’s OK. But I needed to add the code

self.raise_event('x-close-alert', value=True)

to enable entering the password (in the text_box) by pressing Enter.

Unless there’s further improvement, I think I will opt to just use this code here:

def text_box_password_pressed_enter(self, **event_args):
    self.outlined_button_submit_click()

It takes only i single line of code.

Nonetheless, I do appreciate @stucork tip. I did learn how to put focus on an Anvil object like buttons. Thanks for the tip. It might come in handy sometime.

For your use case it looks like focusing the submit button is not what you want.

If your user needs to type in the text box and hit enter to submit the alert, then the pressed enter event is exactly what you want.

You could also do self.submit_button.raise_event("click") (in the pressed_enter event handler), but that’ll just call the self.outlined_button_submit_click() handler in the end anyway.

Thank you, @stucork.

My original intention was to focus on a virtually created button, not the normal physical one. I guess that would need a different solution like that proposed by @stefano.menci using his InputBox and Alert2.

the InputBox example is the same, it focuses a physical button added to the component.

It uses anvil_extras.augment for convenience, which would look like this


from anvil_extras import augment

def form_show(self, **event_args):
    self.submit_button.trigger("focus")

My understanding is that InputBox focuses the default button which you declare

It looks something like this

from anvil_extras import augment


def _set_focus(sender, **event_args):
    sender.trigger('focus')


class InputBox(anvil.Component):

    def show(self):
        ...
        for button in all_buttons:
            if is_default_button(button):
                if not self.focus_set:
                    button.add_event_handler('show', _set_focus)
                break

Ultimately it amounts to the same thing

Thanks for further lessons, I have used InputBox only for creating customized alerts. There is an earlier post here from @stefano.menci about something in his InputBox that might help…that’s why I brought if up.

This is not an issue, this is how the focus works.

Having the focus on a component means the keyboard sends the input to that component.

It’s impossible to have the focus on a button, that is having the keyboard sending input to a button, while entering text in a text_box, that is having the keyboard sending input to the text_box.

Perhaps you are using the word focus, but you mean something else?

Usually these are the things to keep in mind when dealing with input on a form:

  • Where (most of) the keyboard input goes, this is the component with focus
  • What happens when you press enter, sometimes called the default or the enter component
  • What happens when you press esc, sometimes called the cancel component
  • What happens when you press shortcuts like Ctrl+S, etc., which can change when the focus changes component, even if that keystroke doesn’t go to that component

Perhaps by “focus on a button” you mean “firing that button’s click event when pressing enter while the focus is on another component”?

If that is the case, that is also one of the issues I addressed in InputBox.

1 Like

Sorry, gentlemen. I had a wrong idea about ‘focus’.

I thought that after entering the password in the textbox area, it would be possible to shift the focus to the virtual button (‘Submit’) to enter/validate the password. Since I could not find a workaround from @stefano.menci InputBox/Alert2, the solution ended with normal button and its self.button_submit_click.

So, yes. I thought of ‘focus’ for the virtual button but lost it when I switched to the normal button.

Thank you all for the lessons.