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

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

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

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

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

Q&A

解決済

2回答

1798閲覧

node.jsでinnerHTMLを使いたい

bicLighter

総合スコア21

Node.js

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

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

0グッド

0クリップ

投稿2022/02/18 10:44

前提・実現したいこと

現在node.jsのexpressでウェブアプリケーションを作ろうとしています。他サイトから引っ張ってきたカレンダーを使って、サイトに使用したいのですが、このカレンダーはnode.jsで使うことを想定していないため、innerHTMLを使うとエラーが出ます

質問 1
そもそもnode.jsでHTMLを後からいじるという考え方が間違っていますか?(だからinnerHTMLやappendが使えない?)

質問2
このカレンダーはcalendar.jsでテーブルを作成しているのですが、node.jsでこのテーブルを作るにはどうすれば良いのでしょうか?

node.js初心者なため、初歩的な質問かもしれませんが、回答お願いします。

発生している問題・エラーメッセージ

Cannot set property 'innerHTML' of null

該当のソースコード

app.js

1const express = require('express'); 2const mysql = require('mysql') 3const jsdom = require('jsdom-global')() 4const calendar = require('./calendar.js') 5 6app.get('/calendar',(req,res)=>{ 7 res.render('calendar.ejs') 8})

calendar.js

1var today = new Date(); 2var currentMonth = today.getMonth(); 3var currentYear = today.getFullYear(); 4 5function generate_year_range(start, end) { 6 var years = ""; 7 for (var year = start; year <= end; year++) { 8 years += "<option value='" + year + "'>" + year + "</option>"; 9 } 10 return years; 11 12} 13 14var today = new Date(); 15var currentMonth = today.getMonth(); 16var currentYear = today.getFullYear(); 17var selectYear = document.getElementById("year"); 18var selectMonth = document.getElementById("month"); 19 20var createYear = generate_year_range(1970, 2200); 21 22document.getElementById("year").innerHTML = createYear; 23 24var calendar = document.getElementById("calendar"); 25var lang = calendar.getAttribute('data-lang'); 26 27var months = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]; 28var days = ["日", "月", "火", "水", "木", "金", "土"]; 29 30var dayHeader = "<tr>"; 31for (day in days) { 32dayHeader += "<th data-days='" + days[day] + "'>" + days[day] + "</th>"; 33} 34dayHeader += "</tr>"; 35 36document.getElementById("thead-month").innerHTML = dayHeader; 37 38monthAndYear = document.getElementById("monthAndYear"); 39showCalendar(currentMonth, currentYear); 40 41 42 43function showCalendar(month, year) { 44 45 var firstDay = ( new Date( year, month ) ).getDay(); 46 47 tbl = document.getElementById("calendar-body"); 48 49 tbl.innerHTML = ""; 50 51 monthAndYear.innerHTML = months[month] + " " + year; 52 selectYear.value = year; 53 selectMonth.value = month; 54 55 // creating all cells 56 var date = 1; 57 for ( var i = 0; i < 6; i++ ) { 58 var row = document.createElement("tr"); 59 60 for ( var j = 0; j < 7; j++ ) { 61 if ( i === 0 && j < firstDay ) { 62 cell = document.createElement( "td" ); 63 cellText = document.createTextNode(""); 64 cell.appendChild(cellText); 65 row.appendChild(cell); 66 } else if (date > daysInMonth(month, year)) { 67 break; 68 } else { 69 cell = document.createElement("td"); 70 cell.setAttribute("data-date", date); 71 cell.setAttribute("data-month", month + 1); 72 cell.setAttribute("data-year", year); 73 cell.setAttribute("data-month_name", months[month]); 74 cell.className = "date-picker"; 75 cell.innerHTML = "<span>" + date + "</span>"; 76 77 if ( date === today.getDate() && year === today.getFullYear() && month === today.getMonth() ) { 78 cell.className = "date-picker selected"; 79 } 80 row.appendChild(cell); 81 date++; 82 } 83 } 84 85 tbl.appendChild(row); 86 } 87 88} 89 90function daysInMonth(iMonth, iYear) { 91return 32 - new Date(iYear, iMonth, 32).getDate(); 92}

calendar.ejs

1<div class="container-calendar"> 2 <h4 id="monthAndYear"></h4> 3 <div class="button-container-calendar"> 4 <button id="previous">‹</button> 5 <button id="next" >›</button> 6 </div> 7 8 <table class="table-calendar" id="calendar" data-lang="ja"> 9 <thead id="thead-month"></thead> 10 <tbody id="calendar-body"></tbody> 11 </table> 12 13 <div id="calendar_footer" class="footer-container-calendar"> 14 <label for="month">日付指定:</label> 15 <select id="month"> 16 <option value=0>1月</option> 17 <option value=1>2月</option> 18 <option value=2>3月</option> 19 <option value=3>4月</option> 20 <option value=4>5月</option> 21 <option value=5>6月</option> 22 <option value=6>7月</option> 23 <option value=7>8月</option> 24 <option value=8>9月</option> 25 <option value=9>10月</option> 26 <option value=10>11月</option> 27 <option value=11>12月</option> 28 </select> 29 <select id="year"></select> 30 <div id="footer_btn"></div> 31 </div> 32</div>

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

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

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

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

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

guest

回答2

0

documentオブジェクト(と言うかインターフェース)は、
webブラウザ上にしか無くて、
Node.jsには(なんの工夫もなければ)備わってないですよ、って話ですかね。

Document - Web API | MDN

だから、documentオブジェクト前提のメソッド類も全部使えないから、
根本から見直しが必要になりそう。

試しに「nodejs dom操作」などとしてネット検索すると、
Node.js上でも(jQuery使ったりして)DOM操作しようとしている事例がいくつか見つかるので、
そういうのを調べてもらうのが良いかも。
DOM操作できるようパッケージ追加して仕組みを強化してやる、という考え方に見えます。

(肝心の本質的な回答については、Node.jsまわりを普段使っていないので見識がなく、ご提示できません。)

投稿2022/02/19 00:49

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

bicLighter

2022/02/19 08:47

回答ありがとうございます。なるほど、node.jsはサーバー側で動くものなのでwebブラウザ上では使えないということですね、パッケージいろいろ探してやってみます。勉強になりました。ありがとうございます。
guest

0

ベストアンサー

質問 1: そもそもnode.jsでHTMLを後からいじるという考え方が間違っていますか?(だからinnerHTMLやappendが使えない?)

……なるほど、Yes / Noでウミガメのスープみたいな事を続けてると日が暮れると思うので、
JavaScriptとNode.jsの違いが一発でわかるよう解説していきます。
非常に長いし、知ってる箇所もあると思いますが、さらりと読んでみてください。

まずJavaScriptとはなんぞやを掻い摘んで説明してきます。

あなたはChrome等のWebブラウザでWebサイトにアクセスします。
この時ChromeはHTTPリクエストをWebサーバに送信し、
WebサイトはHTML(文書ルールに従って生成された文字列)をHTTPレスポンスとして返します。
(このHTMLというのは単なる文字であって、それ以上でもそれ以下でもないってことね)

Chromeは受け取ったHTMLを画面上に描画する為にメモリ上に展開します。
こうして出来たツリー構造のデータを「DOMツリー」と呼びます。

Chromeは更にJavaScriptのエンジンを起動して「DOMツリー」の編集に備えます。
そしてinnerHTML等の機能を使ってDOMツリーを編集すると、
DOMツリーの変更を検知したChromeが画面をレンダリングしなおします。


続いてNode.jsの説明

Webサイト上で動作するスクリプト言語としてすっかり覇権となったJavaScript
とあるハッカーがJavaScriptをRubyやPythonのような汎用スクリプト言語として使いたいとワガママ言い始めます。

彼はオープンソースとして公開されているChromeのV8エンジンに、
外部ファイルやHTTP通信を自由に行うためのモジュールをC++で作って組み込みました。
それによりChrome等のおまけでDOMツリーを弄るしか能がなかったJavaScriptを、
RubyやPythonのような汎用スクリプト言語にしてしまいました。

この成果物がNode.jsで、ブラウザとは全く関係ないところで動作します。
なのでDOMツリーを操作する為の機能が含まれていません。


何でこんな歴史を説明したかというと、ここからが本番です。

ChromeはJavaScriptのエンジンを搭載していますが、
その用途はDOMツリーを変更して画面の更新を促すしか能がありません。

んで、訪問者のChromeはWebサーバにHTML文字列よこせとHTTP通信投げましたよね。
この時、別に相手側はNode.jsだろうが、Ruby・PHP・Pythonであろうが心底どうでもいいと思ってます。
HTTP通信の仕様に従ってリクエストを受け取ってレスポンスを返してくれれば問題ない。

これがクライアントとサーバの絶対な差です。
お互いが独立している存在であり、双方向で相手が何者なのかに関しては干渉もしないしどうでも良いと思ってます。
インターネットの仕様、HTTP通信の仕様というものが存在してまして、
その仕様通りに動作するならば手段は問わないよみたいな所があります。

この時のサーバが「たまたま」Node.js製のWebサーバだったとしてだから何?なんですよね
HTTPリクエストを受け取ってHTTPレスポンスという文字列を返すのが仕事ですからね。
「DOMツリーを弄る?俺Webサーバだからブラウザも無いんだが?どこのだれが用意してくれんの?」という話なんですよ。

DOMツリーはHTMLを持ち帰ったChromeが
訪問者のパソコンのメモリ空間上に展開して初めて準備されます。

以上の理由からNode.jsでinnerHTML等のDOMを操作する機能を使う事は出来ません。

Node.js触り始めの人が大抵躓くポイントの一つが、
このNode.jsにはDOMツリーがないよ問題です。
質問1の回答としてはこんな所でしょうか。


質問2: このカレンダーはcalendar.jsでテーブルを作成しているのですが、node.jsでこのテーブルを作るにはどうすれば良いのでしょうか?

方法は2通りです。

  1. calendar.jsを捨てて、EJSで相応しいHTMLを生成してやる
  2. クライアントにcalendar.jsを読み込ませて、各々が勝手にDOMツリーを編集して画面に反映せえよと投げる

EJSにはループやif文が使えます。
参考記事: EJS まとめ - Qiita

別に最初からその場に適したカレンダーのHTML作ってやれば
わざわざJavaScriptでDOMツリーを弄らなくても事足りるじゃないですか。
そういうアプローチになります。

EJSのループ機能でtr, th, tdを作ってはめ込んでやれば完成です。
二重ループ作らないといけないのでちょっと大変ですね。
calendar.jsを捨てるとなると重労働だと思います。


後者の方法はcalendar.jsをscriptタグ経由でクライアントに渡してやり、
訪問者のJavaScriptに実行してもらってDOMツリーを編集してもらうプランになります。

Express.jsには日本語のサイトも用意されており、
ファイルとして存在しているcalendar.jsをHTTP通信を通じてクライアントへ送信するような機能もドキュメントで紹介されています。
参考記事: Express での静的ファイルの提供

Expressの公式サイトは神で、上メニューの「解説」と「ガイド」は目を通しておくべきというレベルです。

これで例えば/scripts/calendar.jsにアクセスするとそのファイルがHTTPレスポンスとして配信されるようにしておいて、EJSにこんな感じのタグを追加してやれば良いでしょう。

ejs

1<head> 2 <script defer src="/scripts/calendar.js"></script> 3</head>

普通にスクリプトタグを追加すると
タグの追加した時点でWebサイトのDOMツリー構築をストップして即座にJavaScriptを実行し始めますが
defer属性を付与することでJavaScriptの実行をDOMツリーの構築が完了するまで待ってくれるようになります。

投稿2022/02/18 11:29

編集2022/02/20 04:42
miyabi-sun

総合スコア21158

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

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

miyabi-sun

2022/02/18 11:51 編集

完全に横道ですが Node.jsでやる場合絶対にcalendar.jsを捨てなければならないかというとそうでもなくて、 cheerioみたいなHTML文字列を解析・編集出来るモジュールが存在するんですよね https://www.npmjs.com/package/cheerio まぁ、RubyやPythonと同列に位置する汎用スクリプト言語なんでそのくらい出来てもらわなきゃ困るという感じですが WebサーバがHTMLという文字列を最終的に返せば良いだけなので、一度EJSでひな形HTMLを作ってやってcheerioで再度HTMLを編集して完成形のHTMLを作り直すというアプローチになりそうですね。 実際問題はわざわざHTMLを再度解析して編集して作り直すというのも高コストですし だったらEJSのループとif文で頑張るのが自然だろう。 もしEJSで書くのが面倒なら別のテンプレート言語、例えばPugとかにしなよという話になります。 https://pugjs.org/api/getting-started.html この辺の兼ね合いから、最終的な取るべき手段は解答欄に書いてある2つにしぼりました。
bicLighter

2022/02/19 08:57

回答ありがとうございます。見ず知らずの私に長文で返信していただき誠に感謝です。node.jsとjavascriptの違いがよくわかりました。わかりやすい解説ありがとうございます。実現したいことの回答もすごく参考になりました。とりあえず、後者の方法でやってみたいと思います。
bicLighter

2022/02/19 08:58

exprresの公式サイトのURLもありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問