3

I am creating an AngularJS directive that would bind to a property on the controller scope which contains an 'htm'l string. The string is pulled from a database on the backend. I wish to append the html to an element which contains the directive attribute. Afterwards I would like to drill into the newly created element and surround each 'word' with a span. I have achieved the latter using a jQuery extension function similar to the jQuery highlight plugin. In fact my code is a modification of the original code for this plugin:

jQuery.extend({
highlight: function (node, re) {
    if (node.nodeType === 3) {
        var match = node.data.match(re);
        if (match) {
            var highlight = document.createElement('span');               
            highlight.className = 'highlight';
            var wordNode = node.splitText(match.index);
            wordNode.splitText(match[0].length);
            var wordClone = wordNode.cloneNode(true);
            highlight.appendChild(wordClone);             
            wordNode.parentNode.replaceChild(highlight, wordNode);
            return 1; //skip added node in parent
        }
    } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
            !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
            !(node.tagName === 'SPAN' && node.hasAttribute('ng-class'))) { // skip if already highlighted
        for (var i = 0; i < node.childNodes.length; i++) {
            i += jQuery.highlight(node.childNodes[i], re);
        }
    }
    return 0;
}
});

jQuery.fn.highlight = function (times) {
  var pattern = "\\b([^ ]+)\\b";
  var re = new RegExp(pattern);
  return this.each(function () {
    jQuery.highlight(this, re);
 });
};

The code for my directive:

.directive('spanner', function($compile){
var linkFn = function(scope, element)
{
   element.append(scope.spanner);
   element.highlight();
   $compile(element.contents())(scope);
};

return {
    scope: {
        spanner: '='
    },
    restrict: 'A',
    link: linkFn
  };  
});

This works fine but I am using jQuery. I would prefer to extend jQlite which ships with AngularJs (or perhaps do it in a way which requires no extension at all!). I tried to extend jQlite but failed each time. Can someone suggest a way I can do this without relying on jQuery? I think this would substantially improve my understanding of AngularJs. Thanks in advance.

1 Answer 1

1

Since 90% of the plugin is native script it's a fairly simple port into a directive with no library (jQuery or jQlite) dependencies:

app.directive('highlight', function() {
  /* define highligher variables and function */
  var pattern = "\\b([^ ]+)\\b";
  var re = new RegExp(pattern);

  var highlighter = function(node, re) {
    if (node.nodeType === 3) {
      var match = node.data.match(re);
      if (match) {
        var highlight = document.createElement('span');
        highlight.className = 'highlight';
        var wordNode = node.splitText(match.index);
        wordNode.splitText(match[0].length);
        var wordClone = wordNode.cloneNode(true);
        highlight.appendChild(wordClone);
        wordNode.parentNode.replaceChild(highlight, wordNode);
        return 1; //skip added node in parent
      }
    } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
      !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
      !(node.tagName === 'SPAN' && node.hasAttribute('ng-class'))) { // skip if already highlighted
      for (var i = 0; i < node.childNodes.length; i++) {
        i += highlighter(node.childNodes[i], re);
      }
    }
    return 0;
  }

   /* return directive link function */    
   return function(scope, elem) {
      /* initalize on element */
      highlighter(elem[0], re);
    }    

});

HTML

 <p highlight>Far far away</p>

DEMO

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! This is amazing! It really validates the Angular way of doing DOM manipulations! I am truly grateful! God bless!
could even put the function into a factoryso it doesn't have to be declared for every element...then just inject that factory
That is certainly an efficient way to go about it. Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.