質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Q&A

解決済

3回答

2961閲覧

サーバサイドでinnerHTMLを行うにはどうすれば良いですか?

hojo

総合スコア195

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

0グッド

0クリップ

投稿2016/11/30 14:24

編集2016/12/06 06:07

何故そのようなことがしたいのか?というツッコミを受けそうですが、サーバサイドで出力するHTMLを出力直前にいじりたいのです。

例えばHTMLのdata-numアトリビュートに自動的に番号を割り振るというようなことがしたいのです。

html

1<!-- コンバート前 --> 2<html> 3 <body> 4 <p data-num>first</p> 5 <p data-num>second</p> 6 <p data-num>therd</p> 7 </body> 8</html> 9 10<!-- コンバート後 --> 11<html> 12 <body> 13 <p data-num="0">first</p> 14 <p data-num="1">second</p> 15 <p data-num="2">therd</p> 16 </body> 17</html>

javascript

1let html = fs.readFileSync('test.html') 2let status = null 3for(let i=0; status=html.match(/ data-num[ >]/); i++){ 4 let idx = status.index + 9 5 html = `${html.slice(0, idx)}="${i}"${html.slice(idx)}` 6}

これはなんとか動いたのですが、今度はdata-numが存在するタグのHTML文字列を全て配列で取得したくなってしましました。つまり以下のような結果が欲しいのです。

json

1["<p data-num=\"0\">first</p>", "<p data-num=\"1\">second</p>", "<p data-num=\"2\">therd</p>"]

しかしこれは、正規表現などでは簡単に取得できないと思いました。

サーバサイドでもjQueryを使えるなら簡単にできそうなのですが、さすがにクライアントで動くライブラリをサーバサイドで使うのは無理がありますし、何かいいモジュールがないかと探しているのですが、なかなか見つかりません。

jsdomというライブラリがもしかしたら自分がやりたいことを叶えてくれるかもしれないと思ったのですが、サーバサイドで他のサイトのHTMLを取ってきて操作するためのライブラリのようで、何か大げさな気がしました。

ただinnerHTMLが行えればいいだけなのですが、何かアドバイスいただけないでしょうか

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hojo

2016/12/02 14:37

リンクをつけさせていただきました!ご指摘ありがとうございます。
guest

回答3

0

cheerioのようなjQueryライクなライブラリを利用しても良いですが、実はそのままjQueryが動きます。
http://d.hatena.ne.jp/zebevogue/20120912/1347412619


さて、質問文を読み解くと、設計がおかしいように見受けられます。
HTML自身を後付で修正したいなら、静的なHTMLファイルではなくPug(Jadeから改名)等のテンプレートエンジンを使っているべきでしょう。
これならDOMを後から解析して変更しなくても、テンプレートエンジン内でfor文を使って回している間に質問内容を実現出来ます。

後付けでちょっと出力結果を弄りたいという要望はよくある話なのですが、
この調子であれもこれもと変更箇所が増えると、最後の調整のはずだったものが何十行にも膨れ上がります。
そのうち横並びのページが3つくらいに増えて、コピペのコードだらけになって保守が効かなくなるでしょう。

投稿2016/12/01 01:53

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hojo

2016/12/03 02:31 編集

ややこしくなると思って省略していたのですが、実は既にPugを利用していて(現在はPub→Ejs→Lodashのように移り変わってますが)テンプレートの部分を抜き出したかったのです。 その抜き出した部分的なテンプレートHTMLテキストはクライアントサイドに送りつけて利用しています。
guest

0

自己解決

これがいいかもしれないと思っています。

https://github.com/cheeriojs/cheerio

投稿2016/11/30 15:32

編集2016/12/02 14:37
hojo

総合スコア195

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2016/12/01 01:39

短期的にはそのライブラリで解決すると思います。
hojo

2016/12/06 06:08 編集

ややこしくなると思って伝えていなかったのですが、サーバサイドでinnerHTMLを行いたいというのは少し語弊があり、本当は「テンプレートHTMLの部分を抜き出すためにinnerHTMLに近い処理を行いたい」というのが本題でした。 結果としてcheerioを使ったテンプレートの部分抜き出しは失敗に終わりました。なぜならHTMLの属性値に記述されたテンプレート式```<%= hoge %>```がcheerioを使うと属性として認識され```%>```の式の後に```=""```を勝手につけてしまうからです。 タイトル通りの用途ではcheerioを使うのが最善の方法に思います。しかし、タイトル通りではmiyabi-sunのおっしゃる通り「設計がおかしい」のは明らかであるため、もし同じようなことをしてここにたどり着いた読者さんがいたとしたらその時はPugのようなテンプレートエンジンを利用することをお勧めしたいと思います。
guest

0

自作したので念のため投稿しておきます。

javascript

1 function innerHtml(html, idx) { 2 3 // idxの位置から遡って一番近い開始タグを取得します。 4 let tag = null 5 while( ~(idx=html.lastIndexOf('<', idx)) ) { 6 let result = null 7 if( result = html.slice(idx+1).match(/^\w+/) ) { 8 tag = result.shift() 9 break 10 } 11 } 12 13 // タグを取得できなければ処理を中断します。 14 if(!tag) return '' 15 16 // idxの位置から捜査しタグの内容開始地点を取得します。 17 let begin = idx 18 while( ~(begin=html.indexOf('>', begin)) ) { 19 let result = null 20 if( result = html.slice(idx, begin).match(/[ "\w]$/) ) 21 begin = begin+1 22 break 23 } 24 25 // タグの内容開始地点が見つからなかったら処理を中断します。 26 if( begin == -1 ) return '' 27 28 // タグの内容開始地点からタグの内容終了地点の文字列を取得します。 29 // 閉じタグを発見したら開始地点からそこまでの文字列の中に 30 // 同じタグの開始タグと閉じタグがいくつあるか調べます。 31 // そして、開始タグと閉じタグの数が一致しなければ再度閉じタグを探し直します。 32 // これは対象のタグが入れ子になっていた場合に 33 // 該当する閉じタグを間違えて判定してしまわないよう対処するためのものです。 34 let close = `</${tag}>` 35 let end = begin 36 let inner = '' 37 do { 38 end = html.indexOf(close, end) 39 if( end == -1 ) return '' 40 else end += close.length 41 inner = html.slice(begin, end) 42 console.log(inner) 43 var ocnt = (inner.match(new RegExp(`<${tag}`, 'g')) || {length:0}).length 44 var ccnt = (inner.match(new RegExp(`</${tag}>`, 'g')) || {length:0}).length 45 } while( ocnt!=ccnt ) 46 47 return inner 48 } 49

投稿2016/12/06 06:06

編集2016/12/06 06:09
hojo

総合スコア195

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問