HTML
1<div> 2 <section> 3 <span style="color:#f90">文字列</span><br> 4 <span style="color:#f90">文字列</span><br> 5 <span style="color:#f90">文字列</span><br> 6 #ハッシュタグ 7 #ハッシュタグ #ハッシュTAG#hashタグ<span style="color:#f90">文字列</span> 8 #ハッシュタグ 9 <a href="http://example.jp/#top">#ハッシュタグ 10 </section> 11</div>
(HTMLは簡略化した例であり、実際はもっと入り組んでいます)
jQuery
1const replaced = $('div').html().replace(/(?!(:#...">))([##].+)([\s<]+)/g, ""); 2$('div').html( replaced );
(ひとまず簡略化のため単純に消去 実際はaタグで囲うなどしたい)
というようなイメージで、カラーコード部分(およびリンクのURL部分)以外の「#で始まる文字列」を置換したいのですが
どうもカラーコード部分も置換されてしまいます
(自分の作った文書ではなく依頼されたものであるため、色をrgb(255,…)に書き換えたりcssで色をつけたりはできません)
先にカラーコード部分を
jQuery
1const replaced = $('div').html().replace(/color:#/g, "color:"); 2$('div').html( replaced );
と置換して"color:f90"という形式にしておき、後で戻す(リンクのURLも同様に) …という方法は思い浮かびますが
もしかして、そうするしかないでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/12/01 09:22
回答7件
0
カラーコードに誤爆する
まず、要件に問題がないか確認して下さい。
カラーコードに誤爆する為にカラーコードにマッチしない正規表現にすることを考えたようですが、そうすると次の問題があります。
#bad
などの正しいハッシュタグにもマッチしなくなる- <span title="#foo"> や <a href="http://example.jp/#top"> にはマッチしてしまう
属性値にマッチさせない
問題は属性値にマッチすることなので、カラーコード云々は無視して良いでしょう。
HTML
1<div> 2 <section> 3 <span style="color:#f90">文字列</span><br> 4 <span style="color:#f90">文字列</span><br> 5 <span style="color:#f90">文字列</span><br> 6 #ハッシュタグ 7 #ハッシュタグ #ハッシュTAG#hashタグ<span style="color:#f90">文字列</span> 8 #ハッシュタグ 9 <a href="http://example.jp/#top">#top</a> 10 </section> 11</div> 12<script> 13'use strict'; 14const htmlString = jQuery('div>section').html().replace(/^[^<>]+(?=<)|>[^<>]+(?=<|$)/g, function (match) { 15 return match.replace(/#[^\s<>]+/g, ''); 16}); 17console.log(htmlString); 18</script>
<a> はネスト出来ない
そして、空文字に置換がサンプル故の省略でハッシュタグにリンクを貼る事が目的であった場合、更なる対策が必要となるでしょう。
HTML
1<a href="https://twitter.com/hashtag/javascript">#javascript</a>
a要素は構造上、入れ子に出来ませんので、
HTML
1<a href="https://twitter.com/hashtag/javascript"><a href="https://twitter.com/hashtag/javascript">#javascript</a></a>
このような置換が発生する事は期待に反します。
従って、「a要素の子孫ではないテキストノード値」という条件が必要となります。
a要素の子ノードにマッチさせない
下記はa要素の子ノードではない場合限定の正規表現になります。
JavaScript
1const htmlString = jQuery('div>section').html().replace(/^[^<>]+(?=<(?!/a>))|>[^<>]+(?=<(?!/a>)|$)/g, function (match) { 2 return match.replace(/(#[^\s<>]+)/g, '<a href="https://twitter.com/hashtag/javascript">$1</a>'); 3}); 4console.log(htmlString);
a要素の子孫ノードにマッチさせない
(2018/12/01 20:54追記)
a要素の子孫ノードにマッチさせない場合は、次の正規表現を使います。
HTML
1<div> 2 <section> 3 <span style="color:#f90">文字列</span><br> 4 <span style="color:#f90">文字列</span><br> 5 <span style="color:#f90">文字列</span><br> 6 #ハッシュタグ 7 #ハッシュタグ #ハッシュTAG#hashタグ<span style="color:#f90">文字列</span> 8 #ハッシュタグ 9 <a href="http://example.jp/#top">#top</a> 10 <a href="http://example.jp/#foo"><span>#foo</span></a> 11 </section> 12</div> 13<script> 14'use strict'; 15const htmlString = jQuery('div>section').html(function (i, htmlString) { 16 return htmlString.replace(/^[^<>]+(?=<)|>[^<>]+(?=<|$)(?!(?:</(?!a>)[^>]*>[^<]*)*</a>)/g, function (match) { 17 return match.replace(/(#[^\s<>]+)/g, '<a href="https://twitter.com/hashtag/javascript">$1</a>'); 18 }); 19}).html(); 20 21console.log(htmlString); 22</script>
DOM APIでテキストノードを置換する
正規表現でHTMLタグを置換すると、DOMノードに割り当てられたイベント等の参照系機能の全てが解除されます。
DOM APIでテキストノードを置換すれば、この影響を最小限に抑えることが出来ます。
JavaScript
1'use strict'; 2const xpathResult = document.evaluate('descendant::div/section/descendant-or-self::text()', document.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 3 4for (let i = 0, length = xpathResult.snapshotLength; i < length; ++i) { 5 const textNode = xpathResult.snapshotItem(i); 6 let targetNode = textNode; 7 8 while (targetNode = targetNode.parentNode && targetNode.tagName !== 'A'); 9 10 if (!targetNode) { 11 const df = document.createDocumentFragment(), reg = /([^#]+)|#[^\s#]+/g, text = textNode.data, a = document.createElement('a'); 12 let result; 13 a.appendChild(document.createTextNode('#')); 14 15 while (result = reg.exec(text)) { 16 if (result[1]) { 17 df.appendChild(document.createTextNode(result[1])); 18 } else { 19 const hash = result[0]; 20 21 a.href = 'https://twitter.com/hashtag/' + hash; 22 a.firstChild.data = hash; 23 df.appendChild(a.cloneNode(true)); 24 } 25 } 26 27 if (!result) { 28 textNode.parentNode.replaceChild(df, textNode); 29 } 30 } 31}
Re: elpha さん
投稿2018/12/01 10:51
編集2018/12/01 12:13総合スコア18156
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/12/01 11:40
2018/12/01 11:58
2018/12/01 11:59
0
カラーコードは#12b
か#12abef
のように基本的に3桁か6桁の16進数で出来ています。
※最新のブラウザは4桁、8桁にも対応しており、透明度込みのカラーコードと判断します
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgba
なので.
のようなワイルドカードで指定しまうのではなく、[0-9a-fA-F]
のような16進数の数値として妥当な文字列だけ受け付けることで一歩前進します。
また、+
という風に無限長の文字列を受け付けるのではなく、{3,8}
で指定すれば、
質問文のような回避策を使わなくてもある程度行けるんじゃないですかね?
(5,7桁のような不正なコードも受理されるのでもう少し改良したい所ではありますが)
まずはどのような文字列が来るかを意識して組んでみてください。
投稿2018/11/30 02:06
編集2018/12/05 03:47総合スコア21158
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/12/05 03:17 編集
2018/12/05 03:42
0
こんにちは
この回答は、ご質問のタイトルである 「:の後でない文字列」を探す正規表現 という趣旨からは外れたものになっておりますので、参考程度に留めて頂ければと思います。
ご質問の文中に
(…中略…)というようなイメージで、カラーコード部分(およびリンクのURL部分)以外の「#で始まる文字列」を置換したいのですが
とあり、特に上記の中に
およびリンクのURL部分
とあったことから、質問者さまが実現したいことを掘り下げると、
- 与えられたHTMLに含まれる、
#
で始まる文字列に対して、replaceなどの何らかの操作を行いたいが、この操作はテキストノードに含まれる文字列のみを対象とし、要素の(styleやhrefなどの)属性値に該当の文字列が含まれていても操作の対象外としたい。
ということであると推察しました。(これが間違っているようでしたら、以下の回答は、elpha さんにとって価値のないものと思われますので、無視してください。)
上記の解釈に基づいて、JavaScriptで与件を実現するコードを書こうとすると、まずはテキストノードだけを抽出するコードの実装から考え始めることになりますが、これは正規表現で頑張らずとも、DOM(Document Object Model)を操作するために用意されている豊富なAPIを使った方が楽にできます。(とはいえ、テキストノードだけを対象にして、最終的に行いたい変換まで、何から何まで全部をやってくれる、いわば一発必中の正規表現を書いてやろうという、果敢な挑戦が悪いと言っているわけではありません。)
以下は、テキストノードだけを抽出するコードの一例です。
html
1<div id="outer"> 2 <section> 3 <span style="color:#f90">文字列</span><br> 4 <span style="color:#f90">文字列</span><br> 5 <span style="color:#f90">文字列</span><br> 6 #ハッシュタグ1 7 #ハッシュタグ2 #ハッシュTAG#hashタグ<span style="color:#f90">文字列</span> 8 #ハッシュタグ3 9 <a href="http://example.jp/#top">#ハッシュタグ4</a> 10 </section> 11</div>
javascript
1function getTextNodes(root) { 2 const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false); 3 const textNodes = []; 4 let node; 5 while(node = walker.nextNode()) { 6 if (node.nodeValue.trim().length > 0) { 7 textNodes.push(node); 8 } 9 } 10 return textNodes; 11} 12 13const textNodes = getTextNodes(document.getElementById('outer'));
上記では、 createTreeWalker を使ってテキストノードを収集して配列として返す関数getTextNodes(root)
を作り、これの返り値を textNodes
という変数に入れています。
その後、テキストノードの配列textNodes
の各要素に対して、何らかの処理を行います。以下はその一例です。
textNodes.forEach(t => { t.nodeValue = t.nodeValue.replace(/#([^#\s]+)/g, "{$1}") ; });
上記では、テキストノードに含まれる #ハッシュタグ
という文字列を、 {ハッシュタグ}
にreplace しています。ここまでを動作確認するサンプルを以下に作成しました。
- 動作確認用のサンプル: https://jsfiddle.net/jun68ykt/sg08ru2b/2/
上記のコードでは、 #
の前に :
や /
がある文字列、例えば :#ハッシュタグ
や /#ハッシュタグ
といった文字列も、これらがテキストノードに出現するのであれば、変換の対象になります。以下はそのサンプルです。
- 動作確認用のサンプル: https://jsfiddle.net/jun68ykt/arztwsx9/1/
追記(#タグ をリンクに変換するサンプル)
参考までに、上記の回答に挙げたコードを発展させて、#タグ
をリンクに変換するサンプルを作ってみました。以下です。
- 動作確認用のサンプル: GitHub: jun68ykt/q161170/index.html
上記のindex.htmlを表示すると、以下のようになります。
「タグをリンクにする」ボタンをクリックすると以下のように、 #タグ
にリンクが付加されます。
なお、動作確認のため、各タグのリンク先を Wikipedia の該当ページにしています。また、初期表示の状態から、<a>
の innerText に含まれるタグ #161170
はリンク化の対象外にしています。
投稿2018/12/01 02:32
編集2018/12/02 12:40総合スコア9058
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/12/01 05:24
2018/12/01 12:47
2018/12/02 07:54 編集
0
「入り組んでいる」が具体的にどうか、という話で、挙げた例なら、こうやればできる
(ただし、#ハッシュTAG#hashタグを一つのハッシュタグと認知しているので気をつけてください)
javascript
1 const html = `<div> 2 <section> 3 <span style="color:#f90">文字列</span><br> 4 <span style="color:#f90">文字列</span><br> 5 <span style="color:#f90">文字列</span><br> 6 #ハッシュタグ 7 #ハッシュタグ #ハッシュTAG#hashタグ<span style="color:#f90">文字列</span> 8 #ハッシュタグ 9 <a href="http://example.jp/#top">#ハッシュタグ 10 </section> 11</div>`; 12 const conv = html.replace(/([^:/])[##].*?(?=[\s<])/g, "$1"); 13 console.log(conv); 14/* console 15<div> 16 <section> 17 <span style="color:#f90">文字列</span><br> 18 <span style="color:#f90">文字列</span><br> 19 <span style="color:#f90">文字列</span><br> 20 21 <span style="color:#f90">文字列</span> 22 23 <a href="http://example.jp/#top"> 24 </section> 25</div> 26*/
あとは、replaceの第二引数には関数を渡せるから、長めにマッチさせて#の手前の文字列を見たり、#からはじまる部分がカラーコードっぽいか(っていってもa hrefの#も弾きたいので多分無理だな)みたいは判定をかませることはできると思う。
投稿2018/11/30 02:20
総合スコア12705
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
否定後読み言明というのが使えると簡単なのですが、残念ながらjavascriptでは使えないようです。
そこで、カラーコードとそれ以外とで文字列を分割して処理するのはどうでしょう。
a = '<span style="color:#900">#123</span><br>'; a_list = a.split(/(#...)/);
こうするとa_listの1,3,5...番目にカラーコード、0,2,4,...番目にその間の文字列が入っているはずです。偶数番目の文字列をそれぞれチェックして、末尾が:ならその直後の要素に大して置換処理を行い、最後にリストをくっつけ直して終了。
投稿2018/11/29 19:50
総合スコア13669
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/12/01 12:18
2018/12/01 12:49 編集
0
誰かもいってましたがやはり__否定後読み__でしょう
ES2018での追加で、まだ最新のChromeでしか検証できないですが・・・
もしくはそれを基準にした考え方で代替案としては[こちら](「代替え案」 https://qiita.com/knagisa/items/a37520974e39b25b7a6f)が参考になると思います
HTML
1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>Document</title> 8</head> 9<body> 10 <div class="target"> 11 <section> 12 <span style="color:#f90">文字列</span><br> 13 <span style="color:#f90">文字列</span><br> 14 <span style="color:#f90">文字列</span><br> 15 #ハッシュタグ 16 #ハッシュタグ #ハッシュTAG#hashタグ<span style="color:#f90">文字列</span> 17 #ハッシュタグ 18 <a href="http://example.jp/#top">#ハッシュタグ</a> 19 </section> 20 </div> 21 <div id='btn'>Click</div> 22<script src="./jquery.min.js"></script> 23<script> 24$(document).ready(function(){ 25 $('#btn').click(function(){ 26 const replaced = $('div.target').html().replace(/(?<!color:) [##].+/g, ""); 27 $('div.target').html(''); 28 $('div.target').html( replaced ); 29 }); 30}); 31 32</script> 33</body> 34</html>
投稿2018/12/05 13:28
総合スコア41
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
どのような文字列が来るかを想像しながら、正規表現を見直してみましょう。
カラーコード部分(およびリンクのURL部分)以外の「#で始まる文字列」を置換したいのですが
どうもカラーコード部分も置換されてしまいます
ということなので"
を弾くことで、#カラーコード
も、aタグの中の#
もすっ飛ばせます。
color:
や/#top
に着目するのではなく
color:#f90"
<-の"
/#top"
<-の"
に着目すれば良いのではというアイデアです。
--追記--
ここで、着目を htmlの閉じタグ>
にしない理由は、htmlタグには他の属性が入ってくる可能性があるからです。
なので htmlタグの中に記載された#
にはそのあと必ず"
があるだろうという判断です。
!
に置き換えしてみました。
#か#
で始まって"
を含まず、直後に
か<
か#
か#
がある文字列
javascript
1const replaced = $('div').html().replace(/[##][^\"]*?(?=[\s<##])/g, '!'); 2$('div').html( replaced );
aタグで囲いたい場合は、match()
で上記の正規表現を使って取り出して、前後に文字列を足してあげれば良いのでは?
javascript
1const matched = $('div').html().match(/[##][^\"]*?(?=[\s<##])/g); 2console.log(matched);
--追記--
コメントで指摘のありました"... style="color:#aaa; background-color:#bbb;"
について、一応頑張ってみました
javascript
1const matched = $('div').html().match(/[##][^\";]*?(?=[\s<##])/g); 2console.log(matched);
ですが、他の回答者さんから<a> はネスト出来ない
という意見もありますので、正規表現1発では、あとあと落とし穴に引っかかりそう
ですね、、。
投稿2018/11/30 15:22
編集2018/12/02 12:41総合スコア798
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/11/30 15:54
2018/11/30 16:03
2018/11/30 16:42
2018/12/01 00:43
2018/12/02 10:32
2018/12/02 12:35
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。