CSS: :focus of elements within contenteditable

2019-01-14 08:25发布

问题:

If you have the following HTML:

<div contenteditable="true">
  <p>The first paragraph</p>
  <p>The second paragraph</p>
</div>

Is there a way to style the paragraph that is being edited differently from all other paragraphs in the div that is contenteditable?

div > p:focus { border: 1px solid red }

won't be applied to the paragraph being edited.

Here's a JSFiddle you can play with.

How can I style the paragraph being edited (the <p>-tag) differently?

I'd prefer a CSS-only solution, but perhaps I'll have to use JavaScript.

EDIT:

Since we could not find a pure CSS solution (and using :hover is not a real solution for me) I am now looking for some lines of JavaScript that set the attribute class="focus" on the node that is being edited.

回答1:

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.



回答2:

Interesting question. If it's viable, I'd suggest moving the contenteditable attribute to the ps 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>


回答3:

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>