6

I am trying to detect focus on a child element of a contenteditable element, for the purposes of pure CSS styling. (I know I could detect this with JS, add an extra class and do it that way, but that is so long-winded.)

Basically, I have something along the lines of:

<div contenteditable="true">
  Some text <span class="edit">that</span> goes here.
</div>

I tried CSS along the lines of:

.edit:focus {
  color: #FF0000;
}

I want that span to change colour when the caret enters it, but apparently the focus is only applied to the div set to contenteditable, not to any child thereof. I have tried applying a second contenteditable to the span, but besides being a horribly sloppy approach, it doesn't work anyway.

Is there a solution to this?

Rick
  • 397
  • 3
  • 16
  • Clarification based on an offered solution - I have `div[contenteditable=true]` styling, so the `focus` state needs to remain on the `div`. – Rick Apr 30 '15 at 11:45

4 Answers4

3

My understanding is that the type of elements that can receive focus (automatically) is limited.

See SO Question

One option is to add tabindex to the span.

body {
  font-size: 3rem;
}
div[contenteditable=true] .edit:focus {
  color: #FF0000;
}
<div contenteditable="true">Some text <span class="edit" tabindex="0">that</span> goes here.</div>
Community
  • 1
  • 1
Paulie_D
  • 107,962
  • 13
  • 142
  • 161
  • Really interesting, I've tested this in FF and chrome with no result at all. Does this works for you under certain circumstances? – Nico O Apr 30 '15 at 11:40
  • Thanks @Paulie_D. That actually works, within the limited context as I asked the question. The catch is that I have many other things playing off the `contenteditable`'s focus state… it's own styling, for starters. – Rick Apr 30 '15 at 11:42
  • @Rick and Paulie, I'am very sorry, you are right. Must have made a silly mistake while testing. Confirm this works in FF. My apologies – Nico O Apr 30 '15 at 11:55
  • An interesting twist: it works (running the code snippet in the page) if you click into the `span`, but if you move the caret using the cursor keys, it does not! – Rick Apr 30 '15 at 12:10
2
:focus > .edit { color: #cc0000; }

<div contenteditable="true">Some text <span class="edit">that</span> goes here.</div>
<div contenteditable="true">Some text that goes here.</div>
  • Consider adding some explanation :) – Huey Apr 30 '15 at 11:07
  • 1
    Good idea. The only problem with this solution is, that the `.edit` will always have a color of `#cc0000`as long as the parent contentEditable element is focused. The original problem is to highlight `.edit` *only* when the caret is inside of this certain span. Ups anways. – Nico O Apr 30 '15 at 11:25
2

Because of the limitation that elements within a contenteditable element can't generally receive focus, I suggest faking it by adding a class to your <span> element when the selection is contained within it, which you can do by monitoring the selection for changes (you'll have to use mouse and keyboard events and polling for thoroughness in Firefox until the selectionchange event is implemented in that browser).

var selectionContainer = null;

function updateSelectionContainer() {
  var newSelectionContainer = null;
  var sel;
  if (window.getSelection && (sel = window.getSelection()).rangeCount) {
    newSelectionContainer = sel.getRangeAt(0).commonAncestorContainer;

    // Ensure we have an element rather than a text node
    if (newSelectionContainer.nodeType != 1) {
      newSelectionContainer = newSelectionContainer.parentNode;
    }
  }
  if (newSelectionContainer != selectionContainer) {
    if (selectionContainer) {
      selectionContainer.className = selectionContainer.className.replace(/ ?containsSelection/, "");
    }
    if (newSelectionContainer) {
      newSelectionContainer.className +=
        (newSelectionContainer.className ? " containsSelection" : "containsSelection");
    }
    selectionContainer = newSelectionContainer;
  }
}

if ("onselectionchange" in document) {
  document.onselectionchange = updateSelectionContainer;
} else {
  var el = document.getElementById("editor");
  el.onmousedown = el.onmouseup = el.onkeydown = el.onkeyup = el.oninput = updateSelectionContainer;
  window.setInterval(updateSelectionContainer, 100);
}
div {
  font-size: 200%;
}

.edit.containsSelection {
  color: red;
  font-weight: bold;
}
<div contenteditable="true" id="editor">
  Some text <span class="edit">that</span> goes here.
</div>
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • Thanks @Tim. Monitoring is clearly the only way to go - especially as my real use case is complex enough that CSS's unidirectional nature can't cope. – Rick May 02 '15 at 10:51
0

Just add this instead of :focus. Fiddle.

.edit {
  color: #f00;
}
stanze
  • 2,456
  • 10
  • 13
  • 1
    I **only** want the styling to apply when the caret is moved into the `.edit`, not all the time. – Rick Apr 30 '15 at 09:43