4

Let's say I have a text with mark, such as:

enter image description here

And I would like to disable selecting such that start or end of selection is not in the mark. Therefore, all of these selections should be possible:

enter image description here
enter image description here
enter image description here

On the other hand, these selections should be disabled:

enter image description here
enter image description here

So far, I tried only using some simple css,

mark {
   -khtml-user-select: all;
   -webkit-user-select: all;
   -o-user-select: all;
   user-select: all;
}
This is <mark>marked text</mark>. I want to disable selecting only part of <mark>marked text</mark>.

jsfiddle link

Is there any way to do this? Any answer would be appreciated!


I achieved what I wanted to do, so I share it here.

function checkSelection () {
  var sel = window.getSelection();
  var marks = document.getElementsByTagName('mark');
  for(var i = 1; i < sel.rangeCount; i++) {
    sel.removeRange(sel.getRangeAt(i));
  }
  var range = sel.getRangeAt(0);
  var startnode = range.startContainer;
  var endnode = range.endContainer;
  for (var i = 0; i < marks.length; i++) {
   if (marks[i].contains(startnode)) {
      range.setStartBefore(startnode);
    }
   if (marks[i].contains(endnode)) {
      range.setEndAfter(endnode);
    }
  }
}

document.addEventListener('mouseup', checkSelection)
document.addEventListener('touchend', checkSelection)
This is <mark>marked text</mark>. I want to disable selecting only part of <mark>marked text</mark>.

jsfiddle link

didgogns
  • 255
  • 1
  • 2
  • 7
  • 3
    I don't see any simple way of doing this. You would have to parse the text, use mouse events, add classes etc. – kemotoe Dec 15 '17 at 16:38
  • You could file some bug reports and hope for browser vendors to fix it. https://drafts.csswg.org/css-ui-4/#valdef-user-select-all "If a selection would contain part of the element, then the selection must contain the entire element including all its descendants" - it does not say anything about the select having to start with a click(which MDN tells us) – René Dec 16 '17 at 01:28
  • As for the part "Is there any way to do this", see https://stackoverflow.com/questions/985272/selecting-text-in-an-element-akin-to-highlighting-with-your-mouse (yes) – René Dec 16 '17 at 01:37
  • Please consider marking one of the existing answers as accepted or creating and accepting your own answer so this question can be resolved. – andrewgu Dec 16 '17 at 18:59

2 Answers2

1

I don't think it would be possible to do with CSS alone, you're most likely going to need JavaScript to detect which elements the user began selecting on.


What I did was I used the containsNode method from the Selection API (a Working Draft, may not work with all browsers yet, and may be deprecated) to detect whether the selected range either contains the whole marked element or not at all. If the selection only contains part of a marked element, it would clear the selection using the removeAllRanges method.

function checkSelection () {
  var sel = window.getSelection()
  var marks = document.getElementsByTagName('mark')
  for (var i = 0; i < marks.length; i++) {
    if (sel.containsNode(marks[i], false) !== sel.containsNode(marks[i], true))
      sel.removeAllRanges()  // clear selection
  }
}

document.addEventListener('mouseup', checkSelection)
document.addEventListener('touchend', checkSelection)
mark {
  background: yellow;
}
This is <mark>marked text</mark>. I want to disable selecting only part of <mark>marked text</mark>.

Note that you would need to capture all methods of selection that you want, whether by mouse events and touch events (all that I included), keyboard caret selection, or programmatic selection.


This can be extended and tuned to your liking, but this is my best shot at replicating the behavior you want from your specs. The above snippet doesn't work correctly all the time if one of the selection's bounds begins or ends at the edge of a marked region, but the snippet should demonstrate the basic concept behind what should work, just with a little fine-tuning for edge cases.

andrewgu
  • 1,562
  • 14
  • 23
  • +1 for providing some helpful idea! However, it fails when the selection starts or ends at the border of the mark. – didgogns Dec 16 '17 at 16:59
  • @didgogns I did mention that case in my answer. I've given you the basic code to accomplish the task you asked for and explained why it works. To tune it to specific preferences, you'll have to edit it to behave exactly how you want. – andrewgu Dec 16 '17 at 18:56
-1

user-select CSS property is not yet official and it is only supported by the latest browsers.

Anyways you can use user-select: none; and enclose all text that can not be selected inside a span tag

https://developer.mozilla.org/en-US/docs/Web/CSS/user-select

But you can try something like this

.unselectable {
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
<p>This text can be selected, But <span class="unselectable">this</span> can only be <span class="unselectable">partially se</span>lected</p>

https://developer.mozilla.org/en-US/docs/Web/CSS/user-select

gabehou
  • 69
  • 9