Focusing on nested contenteditable element
The way [contenteditable]
elements are handled by browser made any nested [contenteditable]
not handling any event, the editing host is the former editable parent. See spec:
If an element is editable and its parent element is not, or if an
element is editable and it has no parent element, then the element is
an editing host. Editable elements can be nested. User agents must
make editing hosts focusable (which typically means they enter the tab
order). An editing host can contain non-editable sections, these are
handled as described below. An editing host can contain non-editable sections that contain further editing hosts.
Now as a workaround, you could make focused nested editable element the hosting host by setting any of its editable parent temporaly not editable. See e.g:
$('div.top [contenteditable]').on('focusin focusout', function(e) {
$(this).parents('[contenteditable]').prop('contenteditable', e.type === "focusout");
});
-updated jsFiddle-
Using :focus with nested contenteditable elements
In the end, I added a wrapper element around the spans that cannot be contenteditable. This makes the focus work correctly.
<div contenteditable>
<span contenteditable="false">
<span contenteditable class="focus">content</span>
</span>
</div>
I don't like this solution very much, but I also don't want to add a lot of javascript to this.
contenteditable with nested span. Who has the focus?
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-
CSS: :focus of elements within contenteditable
I think I found a solution.
With the following code snippet you can get the parent element at the current caret position when the selection changes.
var selectedElement = null;
function setFocus(e) {
if (selectedElement)
selectedElement.style.outline = 'none';
selectedElement = window.getSelection().focusNode.parentNode;
// walk up the DOM tree until the parent node is contentEditable
while (selectedElement.parentNode.contentEditable != 'true') {
selectedElement = selectedElement.parentNode;
}
selectedElement.style.outline = '1px solid #f00';
};
document.onkeyup = setFocus;
document.onmouseup = setFocus;
Here I change the outline
property manually but you could of course add a class attribute and set the style via CSS.
You still have to manually check if selectedElement
is a child of our <div>
with the contenteditable
attribute. But I think you can get the basic idea.
Here's the updated JSFiddle.
EDIT: I updated the code as well as the JSFiddle to make it work in Firefox and Internet Explorer 9+, too. Unfortunately these Browsers do not have a onselectionchange
event handler, so I had to use onkeyup
and onmouseup
.
HTML5 - contenteditable=false nested in contenteditable=true
This is a bug in Internet Explorer 11. Section 7.6.1 of the HTML5 specification states:
The
contenteditable
attribute is an enumerated attribute whose keywords are the empty string,true
, andfalse
. The empty string and thetrue
keyword map to the true state. Thefalse
keyword maps to the false state. In addition, there is a third state, the inherit state, which is the missing value default (and the invalid value default).The true state indicates that the element is editable. The inherit state indicates that the element is editable if its parent is. The false state indicates that the element is not editable.
This means that a contenteditable=false
element within a contenteditable=true
element should not be editable.
Furthermore, a contenteditable=true
element within a contenteditable=true
element should have its content editable. Meaning the answer to the question you linked is actually invalid (although when it was posted this may have been the case).
Focusing contentEditable=true parent by clicking on contentEditable=false child nodes
Check this:
HTML contenteditable with non-editable islands
Basic idea is to use empty span with ::before { content:"caption"; }
span.non-editable::before { content:attr(name); background:gold; display:inline-block; }
<div class="editable" contentEditable="true">Some more text here <span class=non-editable name="placeholder"></span> Hi this is my content</div>
Related Topics
Choosing Mobile Web HTML5 Framework
Infinite Scrolling with React Js
Angularjs - Placeholder for Empty Result from Filter
What Cross-Browser Issues Have You Faced
Bootstrap 3: How to Make Head of Dropdown Link Clickable in Navbar
How to Do Anything About "Repaints on Scroll" Warning in Chrome for "Overflow:Scroll" Div
Css3 Keyframe Animations: End and Stay on the Last Frame
Chrome Extension Inject Sidebar into Page
Bootstrap 3 Accordion Collapse Does Not Work on Iphone
How to Set a JavaScript Onclick Event to a Class with CSS
Prevent Select2 from Flipping the Dropdown Upward
JavaScript - Smooth Parallax Scrolling with Mouse Wheel
Modify the Value of Each Textfield Based on Original Value Using Jquery
Set the Default Save as Name for a an <Embed> or <Iframe> That Uses a Blob
Passing Mouse Clicks Through an Overlaying Element <Div>
How to Get CSS Transform Rotation Value in Degrees with JavaScript