Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

contenteditable with nested span. Who has the focus?

This question (to which I added a bounty) is related and gives the context and motivations (my GPLv3 MELT monitor on github; I added at last some README to it).

I am only interested in recent HTML5 compliant browsers (on GNU/Linux), e.g. Firefox 38 at least (and preferably 42) or Chrome 46 (on Debian/Sid desktop, x86-64)

So, suppose I have in my HTML5 page

<div id='myeditdiv' contenteditable='true'>
  <span class='foo_cl'>FOO<span class='bar_cl'>bar</span></span>
</div>

(actually the HTML is generated, and so is the DOM; I'm currently generating on the server side some javascript which constructs the DOM; of course I can change the generators!)

And I am clicking so that the focus come between the two OO. How can I get the DOM element of foo_cl, preferably with Jquery.

Same question when focusing between ar. I want the bar_cl span.

Of course, $(':focus') don't work. It gives the div

FWIW, it is commit 9109ae5b3d168f1 of the MELT monitor.

PS. See my (november 26th 2015) addenda to this question. Probably contenteditable is not useful to me, but tabindex surely is useful!

like image 573
Basile Starynkevitch Avatar asked Oct 31 '22 14:10

Basile Starynkevitch


1 Answers

To make any element focusable, not only interactive content ones, you have to set tabindex attribute.

In your sample, it would be:

<div id='myeditdiv' contenteditable='true'>
  <span class='foo_cl' tabindex="-1">FOO<span class='bar_cl' tabindex="-1">bar</span</span>
</div>

Note: negative tabindex makes element focusable but not tabbable because using tabbing method would start at 0 using absolute value (spec).

Now in jQuery, you could delegate focus event to these elements:

$('[contenteditable]').on('focus', '*', function(e){
    e.stopPropagation();
    console.log(this);
});

-jsFiddle-

As a side note, jQuery UI has a :focusable pseudo selector. If you wish to dynamically set tabindex attribute to not focusable elements, you could use:

$('[contenteditable]').find(':not(:focusable)').attr('tabindex', -1);

-jsFiddle (including jQuery UI)-

If you don't want to include jQuery UI just to get :focusable pseudo selector, you can create your own custom selector:

//include IIFE if not already including jQuery UI
(function () {
    function focusable(element, isTabIndexNotNaN) {
        var map, mapName, img,
        nodeName = element.nodeName.toLowerCase();
        if ("area" === nodeName) {
            map = element.parentNode;
            mapName = map.name;
            if (!element.href || !mapName || map.nodeName.toLowerCase() !== "map") {
                return false;
            }
            img = $("img[usemap='#" + mapName + "']")[0];
            return !!img && $(img).is(':visible');
        }
        return (/^(input|select|textarea|button|object)$/.test(nodeName) ? !element.disabled :
            "a" === nodeName ? element.href || isTabIndexNotNaN : isTabIndexNotNaN) &&
        // the element and all of its ancestors must be visible
        $(element).is(':visible');
    }
    $.extend($.expr[":"], {
        focusable: function (element) {
            return focusable(element, !isNaN($.attr(element, "tabindex")));
        }
    });
})();

-jsFiddle-

like image 121
A. Wolff Avatar answered Nov 02 '22 09:11

A. Wolff