1

As per title, I have a button that can be hit only 3 times and then it will disable (using jQuery) itself.

test.html

<div class="main">
  <input  class="one" type="text" />
  <button class="two" >If you hit me I will disabling myself...</button>
  <button class="three">...and focus should be moved to me!</button>
</div>

test.js

$('.two').on('keyup', function(event) {
    $(event.target).attr('disabled', true);
});

Suppose the user is using the keyboard to do so, by hitting the Enter key

Why the focus does not move to the next button when the currently focused one gets disabled?

Here a link to a fiddle showing what I mean: https://jsfiddle.net/8dyk2b2m/

Edit 1

Suppose that:

  1. You don't know what is the next focusable item but you want it to be focused
  2. I have some cases where the next focusable item in not a sibling of the current one (next() does not work)

Edit 2

My DOM is generated on the fly, that's why I cannot manage case by case but I need a more general algorithm. The stranger thing to me still be that the browser does not manage to move the focus when I disable the field currently focused.

Edit 3

In a comment below, the linked solution from this StackOverflow question does not cover all the cases because the disable action prevent the keyup event to be triggered and - on the other side - the keydown event is to earlier, because when the button is hit a new section is created (obviously by another keydown handler somewhere else and no, I cannot modify that handler directly)

MarmaCinas
  • 176
  • 1
  • 13
  • The fiddle you've provided is not working. Can you please fix it first ? – Kapil Barad Feb 07 '18 at 05:45
  • @KapilBarad, the fiddle is working fine for me. – bharadwaja Gummadi Feb 07 '18 at 05:46
  • @MarmaCinas, try with this I've modified it to click event - https://jsfiddle.net/8dyk2b2m/2/ – bharadwaja Gummadi Feb 07 '18 at 05:47
  • seen the answer here: https://stackoverflow.com/questions/14125842/find-next-closest-focusable-element-using-jquery ? – Kathara Feb 07 '18 at 08:00
  • That's what I already implemented in the private code of my project, but it does not cover the case described by me since the disable action prevent the `keyup` event to be triggered and the `keydown` event is to earlier because when the button is hit a new section is created (obviously by another `keydown` handler somewhere else, and no I cannot modify that handler directly) – MarmaCinas Feb 07 '18 at 08:05
  • My situation is quite hard to explain in all his parts and I am sorry about that. That's also why I edit the original post each time the comments/answers light up something in my head – MarmaCinas Feb 07 '18 at 08:08

4 Answers4

3

Ok, finally I get a good result. I will post here my answer just in case someone would like to do the same or to improve it:

utils.js

function setFocusToClosestTabbableField(target, forward) {
    var promise = $timeout(function () {
        // Find the focused element in case of the given target is not valid
        var $focused = (target instanceof Element || target instanceof jQuery) ? $(target) : $(document.activeElement);

        // Check if the element is visible and enabled
        var isDisabled = $focused.is(':disabled');
        var isHidden   = $focused.is(':hidden');
        if (isDisabled || isHidden) {
            // If the focused element is disabled we have to enable it temporarily in order to find it 
            // in the list of the tabbable elements
            if (isDisabled) {
                $focused.attr('disabled', false);
            }

            // Retrieving now the list of tabbable elements and restore the status of the focused one if needed
            var $tabbables = $(':tabbable');
            if (isDisabled) {
                $focused.attr('disabled', true);
            }

            // Find the index of the current focused element and retrieve the index of the next on tabbable 
            // in the list
            var focusedIndex = $tabbables.index($focused);
            var nextIndex    = focusedIndex + ((forward == null || forward == true) ? 1 : -1);
            if (nextIndex < 0 || nextIndex > $tabbables.length - 1) {
                nextIndex = (forward == null || forward == true) ? 0 : $tabbables.length - 1;
            }

            // Get the next element focusable and put the focus on it
            $focused = $($tabbables.get(nextIndex));
            $focused.focus();

            // If the field is disable force a keyup event because the browser engine prevents it
            if (isDisabled) {
                $focused.keyup();
            }
        }

        // Return the focused element
        return $focused;
    }, 200);

    return promise;
}

main.js

// Registering both keydown and keyup since the browser will prevent the second one if the 
// focused field becomes disabled in a previously attache handler to the keydown event
$('body').on('keydown keyup', function() {
    var key = event.keyCode | -1
    var managedKeys = [
        -1,  // Manually triggered key event
        9,   // Tab
        13,  // Enter
        32   // Space
    ];

    // I check also Enter and Space since if I hit one of them while the focus is on a button and this 
    // button will get disabled, then I have to find the next tabbable field and put the focus on it
    if (managedKey.indexOf(key) > -1) {
        var $target = $(event.target);
        setFocusToClosestTabbableField($target, !event.shiftKey);
    }
});

NOTES

If someone want to discuss about my solution or want to improve it, please do not hesitate! Cheers!

MarmaCinas
  • 176
  • 1
  • 13
0

You can use click and focus for this.

$('.two').on('click', function(event) {
  $(event.target).attr('disabled', true);
  $(this).next().focus();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
  <input class="one" type="text" />
  <button class="two">If you hit me I will disabling myself...</button>
  <button class="three">...and focus should be moved to me!</button>
</div>
4b0
  • 21,981
  • 30
  • 95
  • 142
  • Ok, this ca be a solution but suppose that you don't know what is the next focusable item but you want it to be focused, how can I achieve the result? – MarmaCinas Feb 07 '18 at 06:24
  • See Updated snippet. You can use next() for this.There are so many option like `closest, parent, child` to find which element you want to focus – 4b0 Feb 07 '18 at 06:29
  • Since my original post was lacking some details, I edit it. Please check the changes, thanks! – MarmaCinas Feb 07 '18 at 06:34
0

you have to add focus to next element:

$('.two').on('keyup', function(event) {
 $(event.target).attr('disabled', true);
        $(event.target).next().focus();
});
.main * {
  display: block;
  margin: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
  <input  class="one" type="text" />
  <button class="two" >If you hit me I will disabling myself...</button>
  <button class="three">...and focus should be moved to me!</button>
</div>
xianshenglu
  • 4,943
  • 3
  • 17
  • 34
  • Thanks for your answer, but your code works only if your next focusable item is a sibling of the current focused one. I have some cases where the next focusable item in not a sibling of it – MarmaCinas Feb 07 '18 at 06:28
  • that answer is just a reference,you have to use other selector according to the actual situation or you can list the other situation here and we solve it here – xianshenglu Feb 07 '18 at 06:45
  • I am trying to figure out a generic black box algorithm to cover al cases. I can't manage single situation since my DOM is generated on the fly – MarmaCinas Feb 07 '18 at 07:00
0

Sorry, I can't comment so answering it.

The fiddle is working for me, may be the browser doesnt know what to execute after the function execution. If you have the focus injected in the javascript. at the end of the function execution it works. But why to trigger focus, no answer, why it is not handled by browser.

like

 $('.two').on('keyup', function(event) {     
    $(event.target).attr('disabled', true);                 
    $('.three').focus();                                                  
 });                                                                                                                                                                                                                   
Premalatha
  • 166
  • 6
  • Thank you for you answer, take a look at my [comment](https://stackoverflow.com/questions/48656681/disabling-focused-button-does-not-move-the-focus-to-the-next-focusable-item#comment84312079_48656805) – MarmaCinas Feb 07 '18 at 06:26