HTMLのタグを含む文字列で、タグ以外の文字列だけを置換

文字列を置換するときに、そこにHTMLのタグが含まれていると予期せぬ結果を招くことがあります。

たとえば「class A class B class C」と表示されるHTMLコード

class A <i class="class">class B</i> class C

の文字列「class」だけを、<b>で囲む場合を考えます。このとき単純にString.replace()で

var text = 'class A <i class="class">class B</i> class C';

alert( text.replace( /class/g, '<b>$&</b>' ) );

とすると、class属性やその属性値まで置換されてしまい、

<b>class</b> A <i <b>class</b>="<b>class</b>"><b>class</b> B</i> <b>class</b> C

となってしまいます。これを出力すると「class A class="class">class B class C」のようになり、これは期待した結果とは異なります (出力例はW3C規約に反しない程度に修正してあります)

よってこのような場合にはString.replace()の引数に関数を渡し、置換処理をカスタマイズします。

function Replacer( str, offset, s )
{
    var greater = s.indexOf( '>', offset );
    var lesser = s.indexOf( '<', offset );

    if( greater < lesser || ( greater != -1 && lesser == -1 ) )
    {
        return str;
    }
    else
    {
        return '<b>' + str +'</b>';
    }
}

var text = 'class A <i class="class">class B</i> class C';
var pattern = /class/g;

alert( text.replace( pattern, Replacer ) );

関数Replacer()はパターンにマッチするごと呼び出されます。そしてマッチした文字列がタグの内部にある場合には、置換せずにそのまま文字列を返します。これによりタグ以外の文字列だけが置換されます。この結果は

<b>class</b> A <i class="class"><b>class</b> B</i> <b>class</b> C

となり、これは期待通り「class A class B class C」となります。

タグ内に含まれるタグを削除

または発想を変えて、とりあえず置換した後に、タグ内に含まれるタグを削除する方法もあります。

text.replace( /(<[^<>]+)<b>([^<]+)<\/b>([^<>]+>)/g, '$1$2$3' );
JavaScriptのドキュメントから検索