If you have the following HTML:
The first paragraph
The second paragraph
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
.
Interesting question. If it's viable, I'd suggest moving the contenteditable attribute to the p
s themselves: http://codepen.io/pageaffairs/pen/FHKAC
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style media="all">
div > p:focus {outline: 1px solid red; }
</style>
</head>
<body>
<div>
<p contenteditable="true">The first paragraph</p>
<p contenteditable="true">The second paragraph</p>
</div>
</body>
</html>
EDIT: Here is another version, that causes para returns to generate paras instead of divs: http://codepen.io/pageaffairs/pen/dbyIa
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style media="all">
div:focus {outline: none;}
div > p:focus, div > p:hover {outline: 1px solid red; }
</style>
</head>
<body>
<div contenteditable="true">
<p contenteditable="true">The first paragraph</p>
<p contenteditable="true">The second paragraph</p>
</div>
</body>
</html>
jsFiddle here.
The following is imperfect in the sense that your mouse will need to be in the same area as the selected paragraph (to keep the :hover
argument true), but as this will usually be the case anyway this should be fine - it's the best you're going to get if you want to keep your markup as it is anyway:
CSS:
div[contenteditable="true"]:focus > p:hover {
border: 2px solid red;
}
HTML:
<div contenteditable="true">
<p>The first paragraph</p>
<p>The second paragraph</p>
</div>
jsFiddle here.
If you're able to change the markup, you can use the following simplified selector:
CSS:
p[contenteditable="true"]:focus {
border: 2px solid red;
}
HTML:
<div>
<p contenteditable="true">The first paragraph</p>
<p contenteditable="true">The second paragraph</p>
</div>