Skip to content
Highlight Content in any HTML text
JavaScript Ruby HTML CSS
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib
.gitignore
LICENSE
content_highlight.css
content_highlight.jquery.js
content_highlight.js
readme.md
sample.gif

readme.md

Content Highlight

One way to highlight text in html content (inspired by Medium's highlight feature)

Here is a related ruby gem acts_as_content_highlightable

How it works

How it works

  • Given some html content, assign unique identifiers to every text html node in the content with data attribute data-msnode (this can be customized). ruby sample
  • When text is selected, highlight is created with the first common ancestor html node with a unique identifier for the text range and corresponding start/end offsets. Yes, it works when text range starts in one text node, and ends in another, as long as they share a common ancestral node.
  • The common ancestor node identifier, start offset, end offset are sent to the server, where it can be optionally associated with the current user.
  • The server returns all highlights as JSON and suitable content highlight classes are applied using rangy's class applier module.
  • Highlight removal permissions can also be set by the server. Clicking on a highlight will provide a link to remove the highlight.
  • Removing a highlight will send request the server for acknowledgment

Setup

Include the javascript file(and jquery if needed) files and Rangy modules to your page

<head>
  <!-- You can find the latest rangy release at https://github.com/timdown/rangy/releases -->
  <script type="text/javascript" src="path/to/rangy-core.js"></script>
  <script type="text/javascript" src="path/to/rangy-classapplier.js"></script>
  <script type="text/javascript" src="//path/to/content_highlight.js"></script>
  <script type="text/javascript" src="//path/to/content_highlight.jquery.js"></script>
</head>

If the html nodes in the content are tagged as below

<div id="content_container">
  <p data-msnode="ab14313b">
    <p data-msnode="fc86190d">
      Quisque quis senectus tincidunt elementum nullam viverra,
      <b data-msnode="d3e32d2">
        vehicula tortor <em data-msnode="ded1312e">tempus euismod</em> quisque
      </b>
      senectus fringilla, ut faucibus ultrices tempus ultrices aenean id scelerisque odio bibendum tellus litora felis mi nisl ac aliquam auctor sociosqu.
    </p>
    <p data-msnode="9a772fd1">
      Sociosqu inceptos pellentesque amet felis quis dictum pharetra
      <em data-msnode="daed1212">
        rutrum cras aliquet ut venenatis nostra rutrum
      </em>
      iaculis tristique ultricies amet nullam erat tempus ac dictum, egestas velit rhoncus quisque interdum nullam risus.
    </p>
  </p>
</div>

then

<script type="text/javascript">
  setTimeout(function(){
    var worker = new contentHighlightWorker(document.getElementById('content_container'), {
      nodeIdentifierKey: 'msnode',
      addToServerPath: '/contents/123/add_content_highlights',
      removeFromServerPath: '/contents/123/remove_content_highlights'
    });
    worker.init();
  }, 10);
</script>

or, in jquery

<script type="text/javascript">
  $('#content_container').contentHighlighter({
    nodeIdentifierKey: 'msnode',
    addToServerPath: '/contents/123/add_content_highlights',
    removeFromServerPath: '/contents/123/remove_content_highlights'
  });
</script>

Options Configuration

  • nodeIdentifierKey: data attribute key of unique node identifiers (default: msnode)
  • highlightClass: CSS class added to highlight span (default: content-highlight)
  • highlightIdentifyClassRoot: Root word for the CSS class that is added to highlights (e.g. content-highlight-identifier-11) (default: content-highlight-identifier-)
  • highlightLifetimeClassRoot: Root word for CSS classes that will be added to highlights through its lifetime. This is particularly used to distinguish the highlights by different users (e.g. content-highlight-lifetime-me vs content-highlight-lifetime-others) (default: content-highlight-lifetime-)
  • highlightActiveClass: CSS class added to active highlight (default: content-highlight-active)
  • popTipClass: CSS class added to the popTip that is shown when highlight is clicked (default: content-highlight-poptip)
  • popTipDefaultHead: Text header in the popTip (default: Highlight)
  • addToServerPath: Path to Add highlight to Server (default to the element's data attribute: element.dataset.addhighlightspath)
  • removeFromServerPath: Path to Remove highlight from Server (default to the element's data attribute: element.dataset.removehighlightspath)
  • readOnly: Highlights are readonly and not clickable (default: false)

Adding, Removing and Fetching highlights

See rails controller sample

  • Add highlights When a text range is selected, the following params are sent to addToServerPath
{  
  :content => "hello world was selected",
  :common_ancestor_identifier_key => "msnode", # nodeIdentifierKey
  :common_ancestor_identifier => "fc86190d",
  :common_ancestor_node_type => "p", #text node p
  :start_offset => 33,
  :end_offset => 123,
  :backward => true, # denotes the direction of selection
}

After storing this highlight, the server must return all the highlights relevant to that content as json

[
  {
    "identifier": highlight.id,
    "description": "Highlighted by #{highlight.user.full_name}",
    "can_cancel": (highlight.user_id == current_user.id or current_user.is_admin?),
    "life_time_class_ends": ((highlight.user_id == current_user.id) ? "me" : "others"),
    "content": highlight.content,
    "backward": highlight.selection_backward,
    "start_offset": highlight.startnode_offset,
    "end_offset": highlight.endnode_offset,
    "common_ancestor_identifier": highlight.container_node_identifier,
    "common_ancestor_node_type": highlight.container_node_type
  }
]

And, highlights will be shown only if and only if the start_offset, end_offset, content, common_ancestors all match.

  • Remove highlights When a highlight is clicked, a pop tip is shown with a link to remove that highlight if the user has permission to do so send the highlight's id to removeFromServerPath
{
  content_highlight_id: 123,
}

After removing the highlight, the server acknowledges by returning the removed highlight

{
  "identifier": highlight.id,
  "description": "Highlighted by #{highlight.user.full_name}",
  "can_cancel": (highlight.user_id == current_user.id or current_user.is_admin?),
  "life_time_class_ends": ((highlight.user_id == current_user.id) ? "me" : "others"),
  "content": highlight.content,
  "backward": highlight.selection_backward,
  "start_offset": highlight.startnode_offset,
  "end_offset": highlight.endnode_offset,
  "common_ancestor_identifier": highlight.container_node_identifier,
  "common_ancestor_node_type": highlight.container_node_type
}
  • Fetching highlights Since addToServerPath returns all the highlights anyways, we send empty params and receive all the highlights from the server as json

Dependencies

Text selection is supported by Rangy

License

MIT

You can’t perform that action at this time.