2

I'm trying to write some code to let the user highlight parts of a website's content. To do so, I replace the user selection (selectedText) by some HTML (<div class = "highlight">selectedText</div>) - and it works (cf. code below).

The problem is that wrapping the selected text in this way overrides the rest of the CSS and ruins the layout, regardless of the HTML tag used as a container (div, span, em, mark, hr, ...). I apologize if the solution to this problem is obvious, I'm quite new to web dev.

JS

document.addEventListener("mouseup", function() {
  var selectedText = getSelectedText();
  if (selectedText.length > 0) {
    div = document.createElement("div");
    div.innerHTML = selectedText.trim();
    div.classList.add("highlight");
    replaceSelection(div);
  }
});

CSS

div.highlight {
  background-color: #F5E283;
  display: inline;
}

// Highlighter
// Selection: thx http://www.javascriptkit.com/javatutors/copytoclipboard.shtml :)
function getSelectedText() {
  var selection = "";
  if (window.getSelection) {
    var selection = window.getSelection().toString();
  }
  return selection
};
// Replacement: thx https://stackoverflow.com/questions/3362747/how-to-insert-an-element-at-selected-position-in-html-document/3363087#3363087
function replaceSelection(replacement) {
  var range, html;
  if (window.getSelection && window.getSelection().getRangeAt) {
    range = window.getSelection().getRangeAt(0);
    range.deleteContents();
    range.insertNode(replacement);
  } else if (document.selection && document.selection.createRange) {
    range = document.selection.createRange();
    html = (replacement.nodeType == 3) ? replacement.data : replacement.outerHTML;
    range.pasteHTML(html);
  }
}

document.addEventListener("mouseup", function() {
  var selectedText = getSelectedText();
  if (selectedText.length > 0) {
    console.log(selectedText);
    mark = document.createElement("mark");
    mark.innerHTML = selectedText.trim();
    mark.classList.add("highlight");
    replaceSelection(mark);
  }
});
div.highlight {
  background-color: yellow;
}
<! DOCTYPE HTML>
<html>

<body>
  <h1>Please, select this h1</h1>
  <p>and this text at the same time</p>
  
  <h1>Now, select <i>a part of this one</i></h1>
  <p>with this text</p>
  <p></p>
</body>

</html>
westload
  • 46
  • 6
  • 1
    Hi, can you please give a better explanation of what you mean by "overrides the rest of the CSS and ruins the layout" ? – FabioG May 01 '20 at 09:19
  • 1
    Hey :) I added a code snippet at the end of my post, I think it'll be clearer than my explanations. I meant to say that the highlighted text behaves like a new "block", regardless of the pre-existing tags and classes. – westload May 01 '20 at 09:25
  • 1
    if you mean the loss of existing white-spaces, you should probably consider a way to preserve those spaces before doing the `mark.innerHTML = selectedText.trim();` – FabioG May 01 '20 at 09:39
  • 1
    Yes, indeed (but it's not really my problem here). If we take the example of the snippet, I would like to be able to highlight the H1 **and** the text while keeping the H1 in its place. I guess that's more of a CSS issue than a JS problem.Thanks for your answers. – westload May 01 '20 at 09:48
  • 1
    The thing is you're replacing the entire content of the range selection, thus losing the html tags

    and

    in the example. you need to find a way to go through each element inside the selection and create the highlight inside all of them. It's not trivial, I've been trying to play a bit with it as it's an interesting challenge but with limited success so far. if you want to take a look at what I've tried and try to improve on it - https://jsfiddle.net/FabioSG/59gsdfcx/5/

    – FabioG May 01 '20 at 18:16
  • How didn't I think of it! Thanks a lot for your help, I'll try to find a solution based on this. – westload May 02 '20 at 11:18

1 Answers1

1

I think I understand the issue. If you have styling on the h1 element, highlighting the text will remove the h1 styling as the highlighted element becomes a child of h1. Try this if you, for example, want to change the color:

h1, h1 div.highlight{
  color: red;
}

or

h1{
  color: red;
}

div.highlight{
  color: inherit;
  background-color: #F5E283;
  display: inline;
}

I also noticed an issue with the trim - if I select 'this ' in the string 'select this h1', the trailing space is trimmed and you get 'select thish1', which i prop not what you want.

Will it work without selectedText.trim()?

Cool feature so it gets fully working!

LaZza
  • 360
  • 3
  • 7