JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://underscorejs.org/underscore.js"></script>
<script src="http://epeli.github.io/underscore.string/lib/underscore.string.js"></script>
<h1>Être ou ne pas être, là est la question.</h1>

CSS

span.red {color:red;}

JavaScript

_.mixin(_.str.exports());
normalizeText = function(s){
    var r=s.toLowerCase();
    r = r.replace(new RegExp("[àáâãäå]", 'g'),"a");
    r = r.replace(new RegExp("æ", 'g'),"ae");
    r = r.replace(new RegExp("ç", 'g'),"c");
    r = r.replace(new RegExp("[èéêë]", 'g'),"e");
    r = r.replace(new RegExp("[ìíîï]", 'g'),"i");
    r = r.replace(new RegExp("ñ", 'g'),"n");                            
    r = r.replace(new RegExp("[òóôõö]", 'g'),"o");
    r = r.replace(new RegExp("œ", 'g'),"oe");
    r = r.replace(new RegExp("[ùúûü]", 'g'),"u");
    r = r.replace(new RegExp("[ýÿ]", 'g'),"y");
    return r;
};
highlight = function(text, pattern) {
    var textNorm = normalizeText(text),
        terms = pattern.split(/[\s,]+/),
        matches = [],
        html = text;
    
    // Find matches
    _.each(terms, function(term) {
        term = _.trim(term);
        var termNorm = normalizeText(term),
            index = textNorm.indexOf(termNorm);
        while (index >= 0) {
            matches.push({
                index: index,
                term: term,
                termNorm: termNorm
            });
            index = textNorm.indexOf(termNorm, index+termNorm.length);
        }
    });
    // Last matches first
    matches = _.sortBy(matches, function(match) {return -match.index;});
    // Highlight them
    _.each(matches, function(match) {
        html = html.substring(0, match.index) +
            '<span class="red">' +
            html.substring(match.index, match.index + match.term.length) +
            '</span>' +
            html.substring(match.index + match.term.length);
    });
    
    return html;
};
$('h1').html(highlight($('h1').text(), 'etre la'));