Highlight on html , js + json - POC
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

I have a html document like this
http://crts-versions.s3.amazonaws.com/06_condo_508_apr2016/1.html

which is loaded in to a system that we are going to let people "highlight" on. id like to see if we can make a POC where we load this page, some js that you write, which will give me a "pen" so i can highlight some text and have it persist to a json object.

What im looking for in particular is how best to attach the "highlight" to the document without inserting spans in to the actual document itself. aka i want to store highlights separate from the html. In looking at the document i see a lot of IDs on basically each row of elements. I was thinking a coordinate system like

ID+offset in reality it would look something like this
tg_1,6,6

this basically means find ID 'tg_1' and then count 6 spaces over, start highlight, then count 6 more spaces and end highlight. the result would be something visually like https://db.tt/YZA7nBne9l

I dont know if this is the best method and if it could handle things like a highlight spilling in to the next row etc.. but would be interested in your ideas and POC

POC should let me see the json data structure so i can copy it, and also let me load one which was previously done. ideally the highlight data is getting shown here without saving or clicking a button and updating in real time (as i drag highlighter, its updating coordinates)

the final js should work in the latest stack of browsers Chrome, IE11, FF etc.. ideally we dont use jquery for this but if required then ill take it.

Ill award 2 winners on this. 1 for $100 and the second for $50

can you please clarify more the bounty? you want to save the highlights in real time in your server? and after refreshing, re highlight them? is that what you want exactly?
Houcem B. A. Chlegou 3 months ago
Thanks for the question - No not to a server. just to the viewer of the local json object. this bounty has zero server requirements everything local makes it easier. Ideally i can see the json needed to make all of the highlights in some window on the page, i can use this for viewing or loading json to restore highlights or backup some tests from the POC you are making. For example if i draw 20 highlights, i can see them on the page and then copy the json needed to recreate that.
Qdev 3 months ago
1) i want you tell me what do you mean by POC, i made a search, but nothing found about POC. 2) so as you said, you want to save the highlights, in a json, then you want to keep all the highlights in a current webpage view (this what make sense for me). 3) do you want to save that in cookies in case he refresh the page, they will be highlighted again (his last work) or not?
Houcem B. A. Chlegou 3 months ago
1- POC = proof of concept 2- not needed but thanks for asking!
Qdev 3 months ago
hmm, after thinking, i think that the coordinate system you spoke about, is the best approach, to reach that, and basically, we use span tags, and your goal here is to avoid using spans, right? then i have two ideas, 1) i assume, that you have your documents stored, and you don't want to touch them, want to keep the original content, so WHEN LOADING the webpage, we can do it, (inserting span tags in the current webpage view not in your html docs. it's easier i think). 2) trying to find out how to do that with javascript without inserting span tags, i think we can do it in a way or another.
Houcem B. A. Chlegou 3 months ago
Let me clarify on the spans. I'm fine if the Javascript puts them there when it runs/renders the json. I just can't store the HTML with the spans in it statically. The other way to do this (idea from Dropbox commenting) is to put a text layer div over the background "page" and then store highlighting on top of the HTML document. This would decouple the highlight from the HTML itself. It would still need coordinates but they wouldn't rely on a container ID but rather just some sort of x,y on the overlay. Hope this helps.
Qdev 3 months ago
so my approach, is to have a json array, that contain coordinates of all highlighted words, then for every element, we process it with CSS, to give it the highlight properties. (every highlighted element, will be inserted in a tag and styled through CSS). if this approach work for you,i will do it.
Houcem B. A. Chlegou 3 months ago
awarded to kostasx

Crowdsource coding tasks.

3 Solutions


The easiest way is to put the spans into the document on page load. This does not mean the spans are getting saved to disk.

Then you can just highlight as usual with CSS, and not worry about x,y, etc.

Creating highlights:

Where someone selects text, that's where the spans go.
You can use a text-highlighting library to handle the dirty work of adding and removing highlights.

Loading highlights:

If you'd like to load the highlights (say after a page reload) then all you would need is the character position for the startpoints and endpoints of the spans. Store that in your localstorate, cookies, etc. Then your script can just add the spans again dynamically.

Getting the text content:

You can use regular JS or JQuery to get the text content of the text inside the spans, and save that into some variable / object.


That's the theory.


There's a JavaScript library called Rangy that does pretty much exactly what you want.

To add the functionality to the example page, add the following in <head>...</head>:

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-core.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-classapplier.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-textrange.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-highlighter.min.js"></script>

<script type="text/javascript"> 
  var highlighter;
  window.onload = function() {
    rangy.init();
    highlighter = rangy.createHighlighter();

    highlighter.addClassApplier(rangy.createClassApplier("highlighted", {
      ignoreWhiteSpace: true,
      tagNames: ["span", "a"]
    }));

    document.onkeypress = function (event) {
      if (event.key == "h" || event.key == "H") {
        highlighter.highlightSelection("highlighted")
      } else if (event.key == "u" || event.key == "U") {
        highlighter.unhighlightSelection();
      }
    }
    // Example highlight
    restoreHighlights({highlights: "type:textContent|8698$8940$1$highlighted$|9441$9662$4$highlighted$|10723$10915$2$highlighted$|11274$11505$3$highlighted$"});
  }

  function saveHighlights() {
    return {
      highlights: highlighter.serialize()
    };
  }

  function restoreHighlights(json) {
    highlighter.deserialize(json.highlights);
  }
</script>

And add .highlighted {background-color: #ff0;} in the CSS (<style type="text/css">...</style>). You can apply these changes to a local copy if you save the html file (that's how I tested it).

Then, pressing h on the keyboard will highlight any selected text and pressing u will unhighlight any highlight that the selected text reaches.

In JavaScript, calling saveHighlights() returns an object containing a string in Rangy's own serialization format. The highlights can then be restored using restoreHighlights(...) with the object as argument.

Just make sure to call restoreHighlights(...) only after the document is done loading (e.g. in the window.onload, function, like I did). That's because rangy cannot be initiated until the body has been loaded.

Putting the result of highlighter.serialize() (which is just a string) in an object is superfluous, that was just to satisfy your requirement to use JSON. You could just use highlighter.serialize() and highlighter.deserialize(...) directly instead.

This sounds interesting. Would you be able to make a demo with our HTML file? Thanks!
Qdev 3 months ago
Sure, here it is: http://platinumbobo.s3-website-us-east-1.amazonaws.com/highlight/ Let me know if you'd like me to change the way it works.
PlatinumBobo 3 months ago
Thanks for the demo on this, ill have feedback asap
Qdev 3 months ago
Winning solution

Here is my solution. It is based on the Shadow DOM, so the main document body with the text remains intact, and all highlighted text along with the special SPAN tags appear in the Shadow DOM copy (clone) of the main element. You can display the main document, by removing the CSS: #p1 div.t { color: transparent; }

Usage:
1) Highlight words or sentences by selecting the text and pressing Ctrl+H or hitting the Highlight button
2) Export the JSON with the highlighter items by pressing the button "Copy JSON of highlighted items". Copy the text that appears.
3) Refresh the browser and hit the "Click to paste JSON of highlighted items" button. Paste the JSON and press enter.

Codepen: https://codepen.io/kostasx/pen/jmVQJJ?editors=1000

Let me know what you think.

(Currently tested and works only in Chrome)
kostasx 3 months ago
(For Firefox/Safari, you'll need to disable the .t CSS class that sets the main text color to transparent. I'm still trying to figure out how to fix the highlighted text color.)
kostasx 3 months ago
@kostasx this is really solid work. ill show it to my team in a few hours!
Qdev 3 months ago
quick question, if we are running in angularjs do you see any conflicts with the shadow dom here?
Qdev 3 months ago
Although I am not very familiar with Angular, I don't see the reason why there should be any conflict. Let me know if your team does any tests.
kostasx 3 months ago
Is there a way to group coordinates to represent a single highlight . For example if I highlight two paragraphs in a single action. The data will be grouped... thoughts?
Qdev 3 months ago
disregard last question, i see it is grouped already.. needed to format it to see but...
Qdev 3 months ago
View Timeline