#Node.jsでチャットルームを作りたい
というわけで作っていきたいと思います。
方針としてはNode.jsでlocalhost:3000に鯖を立てて、
そこにHTML/CSS/JavaScriptを適用します。
##ファイル構成
今回の構成は以下の通りです。
- index.js
- index.html
- node_modules
|
- ..etc
- pic
|
- ..etc
- js
|
- window.js
- socket.js
- css
|
- window.css
- package.json
##サーバ構築
下記コード内で require('module') をしている
nodeモジュールを npm install などでインストールしてください。
var http = require('http');
var fs = require('fs');
var path = require('path');
var mime = {
".html": "text/html",
".css": "text/css",
".js": "text/javascript",
// 読み取りたいMIMEタイプはここに追記
// MIMEを宣言しないとクライアント側のHTMLに通した
// JSやCSSが取得できない
};
//socket.ioモジュールの読み込み
var socketio = require("socket.io");
//HTTPオブジェクトを生成
var http_server = new http.createServer(function(req, res {
if(req.url == '/'){
filePath = '/index.html';
}else{
filePath = req.url;
}
//ここで絶対PATHの取得
var fullPath = __dirname + filePath;
res.writeHead(200, {"Content-Type": mime[path.extname(fullPath)] || "text/plain"});
fs.readFile(fullPath, function(err, data){
if(err){
//エラー時の応答
}else{
res.end(data, 'UTF-8');
}
});
}).listen(3000);
console.log('Server running at http://localhost:3000/');
//イベントハンドラを設定
var io = socketio.listen(http_server);
io.sockets.on("connection", function (socket){
//メッセージ送信(送信者にも送られる)
socket.on("C_to_S_message", function (data){
io.sockets.emit("S_to_C_message", {value:data.value});
});
//ブロードキャスト(送信者以外の全員に送信)
socket.on("C_to_S_broadcast", function (data){
socket.broadcast.emit("S_to_C_message", {value:data.value});
});
//切断したときに送信
socket.on("disconnect", function (){
io.sockets.emit("S_to_C_message", {value:"user disconnected"});
});
});
##HTML/CSS/JavaScriptについて
今回、クライアントサイドとサーバサイドの結合に
少々手間をとりました。
その理由として、
HTMLに紐づけたjsにScoket.ioの
イベントハンドラ設定したらサーバサイドに認識されなくね?
まあこれですね、仕方なくHTMLに素書きしました...。
いい方法、教えていただきたいです(懇願)
###必要なもの
- socket.io.js(socket.js)
https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js
以上のリンクを参照するか、落としてください。
今回はsocket.jsとしてjsファイル内に置いています。
とりあえず、コードは以下の通りです。
ボタンの部分はどんなインスタンスでもいいです。
今回は丸いボタン風画像をボタンにできるように実装しています。(ゴミ箱も同様)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Access-Control-Allow-Origin" content="*">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta name="application-name" content="Olt_chat">
<link rel="stylesheet" type="text/css" href="css/window.css">
</head>
<body>
<div id = "window_panel" class = "window_panel">
<div id="headder_panel"class="headder_panel">
<a href="#" id="btn_clean" onclick = "clean_localStr()">
<div id = "btn_cln">
<img src="pic/clean_btn.png" style="width:100%; height:100%;">
</div>
</a>
</div>
<div id="timeline_panel" >
<div id="obj" class ="obj"></div>
</div>
<div id="input_panel" class = "input_panel" >
<textarea id = "textInput_area" class ="textInput_area"></textarea>
<a href="#" id="btn_sample" onclick = "setTimeout(btn_push(), 300);">
<div id = "btn_pic">
<img src="pic/button_post.png" style="width:100%; height:100%;">
</div>
</a>
</div>
</div>
</body>
<script type="application/javascript" src="js/socket.js"></script>
<script>
var s = io.connect('http://localhost:3000');//ローカル
//サーバから受け取るイベント
s.on("connect", function () {});//接続時
s.on("disconnect", function (client) {});//切断時
s.on("S_to_C_message", function (data) {
addMessage(data.value);
});
//クライアントからイベント送信時のイベントハンドラ
function sendBroadcast(text) {
let msg = text //取得
s.emit("C_to_S_broadcast", {value:msg}); // サーバへ送信
object_make(msg);
console.log(msg);
}
function addMessage (value,color,size) {
let msg = value.replace( /[!@$%<>'"&|]/g, ''); //タグ記号とかいくつか削除
object_make(msg);//この関数を用いてチャットを表示
}
</script>
<script type="application/javascript" src="js/window.js"></script>
</html>
描画処理というか全体的にネイティブJS使ってますが、
そこらへんは、レスポンシブデザインの処理の速さを求めた結果なのでご了承(自己満
....コメント打つのもだるくなってるレベルですが...。
画像のpathさえ入れてやればちゃんと動きます。
var count = 0;
var touch_position_h;
var touch_prevent_flag = 0;
document.getElementById("timeline_panel").ontouchstart = function(event){
if (touch_prevent_flag === 0){
}else{
touch_position_h = event.changedTouches[0];
}
}
document.getElementById("timeline_panel").ontouchmove = function(event){
if (touch_prevent_flag === 0){
event.preventDefault();
}else{
}
}
window.onload = function(){
window_getsize();
}
window.onresize = function(){
window_getsize();
}
function window_getsize(){
var size_h = window.innerHeight;
var size_w = window.innerWidth;
if(size_h > size_w){
var element = document.getElementById("window_panel");
element.style.height = size_h + 'px';
element.style.width = size_w + 'px';
var element = document.getElementById("headder_panel");
let headder_size_h = size_h*0.085;
element.style.height = headder_size_h + 'px';
element.style.width = size_w + 'px';
element.style.top = 0 + 'px';
var element = document.getElementById("btn_clean");
let btncln_size_h = headder_size_h;//0.2*0.5*0.2
element.style.height = btncln_size_h*0.75 + 'px';
element.style.width = btncln_size_h*0.75 + 'px';
element.style.bottom = btncln_size_h*(0.1) + 'px';
element.style.right = btncln_size_h*(0.4) + 'px';
var element = document.getElementById("input_panel");
let IPsize_h = size_h*0.14;
element.style.height = IPsize_h + 'px';
element.style.width = size_w + 'px';
element.style.bottom = 0 + 'px';
var element = document.getElementById("timeline_panel");
let TLsize_h = size_h - IPsize_h - headder_size_h;
element.style.height = TLsize_h + 'px';
element.style.width = size_w + 'px';
element.style.bottom = size_h - TLsize_h - headder_size_h + 'px';
var element = document.getElementById("btn_sample");
let btn02_size_h = size_h*0.12;
element.style.height = btn02_size_h*0.85 + 'px';
element.style.width = btn02_size_h*0.85+ 'px';
element.style.bottom = btn02_size_h*(0.25/2.5) + 'px';
element.style.right = btn02_size_h*(0.25) + 'px';
var element = document.getElementById("textInput_area");
let textarea_size_h = size_h*0.12;
element.style.height = textarea_size_h*0.75 + 'px';
element.style.width = size_w - btn02_size_h*0.85 - btn02_size_h*(0.25)*3 + 'px';
element.style.bottom = textarea_size_h*(0.25/1.5) + 'px';
element.style.right = btn02_size_h*0.85+btn02_size_h*(0.25)*2 + 'px';
element.style.max_height = textarea_size_h*0.75 + 'px';
element.style.max_width = size_w*0.675 + 'px';
}
else if(size_h < size_w){
var element = document.getElementById("window_panel");
element.style.height = size_h + 'px';
element.style.width = size_w + 'px';
var element = document.getElementById("headder_panel");
let headder_size_h = 0;
element.style.height = headder_size_h + 'px';
element.style.width = size_w + 'px';
element.style.top = 0 + 'px';
var element = document.getElementById("input_panel");
let IPsize_h = size_h*0.215;
element.style.height = IPsize_h + 'px';
element.style.width = size_w + 'px';
element.style.bottom = 0 + 'px';
var element = document.getElementById("timeline_panel");
let TLsize_h = size_h - IPsize_h - headder_size_h;
element.style.height = TLsize_h + 'px';
element.style.width = size_w + 'px';
element.style.bottom = size_h - TLsize_h - headder_size_h + 'px';
var element = document.getElementById("btn_sample");
let btn01_size_h = size_h*0.2;
element.style.height = btn01_size_h*0.85 + 'px';
element.style.width = btn01_size_h*0.85 + 'px';
element.style.bottom = btn01_size_h*(0.25/2.75) + 'px';
element.style.right = btn01_size_h*(0.25) + 'px';
var element = document.getElementById("btn_clean");
let btncln_size_h = size_h*0.085;
element.style.height = btncln_size_h*0 + 'px';
element.style.width = size_w*0 + 'px';
element.style.bottom = btncln_size_h*(0.075) + 'px';
element.style.right = btncln_size_h*(0.4) + 'px';
var element = document.getElementById("textInput_area");
let textarea_size_h = size_h*0.2;
element.style.height = textarea_size_h*0.75 + 'px';
element.style.width = size_w - btn01_size_h*(0.25)*3 - btn01_size_h*0.85 + 'px';
element.style.bottom = textarea_size_h*(0.25/2) + 'px';
element.style.right = btn01_size_h*(0.25)*2+btn01_size_h*0.85 + 'px';
element.style.max_height = textarea_size_h*0.75 + 'px';
element.style.max_width = size_w*0.675 + 'px';
}
}
function btn_push(){
var element = document.getElementById("textInput_area");
let send_text = element.value;
if(element.value!==null && element.value!=false){
element.value=null;
}else{
return;
}
setTimeout(sendBroadcast(send_text), 1000);
}
var count_onclick_btnSample = 0;
var messageLast_containerSizeHeight = [];
function object_make(text){
let TLsize_h = document.getElementById("timeline_panel").height;
let TLsize_w = document.getElementById("timeline_panel").width;
var str_tmp = 0 + '';
if(count_onclick_btnSample > 0){
str_tmp = TLsize_h + messageLast_containerSizeHeight[count_onclick_btnSample];
}else {
var str_tmp_s;
str_tmp_s = str_tmp;
}
let object = document.createElement('div');
let object_s = document.getElementById("obj");
object_s.innerHTML = ['<div id="message_container">'
+ '</div>'
+ '<div id = "message_0" >'
+ '<p>'+ text +'</p>'
+ '</div>'
+ '</div>'
+ '</div>'].join("");
object_s.append(object);
var element = document.getElementById("message_container");
var element1 = document.getElementById("message_0");
messageLast_containerSizeHeight.splice(count_onclick_btnSample+1, 0, str_tmp);
count_onclick_btnSample = count_onclick_btnSample + 1;
}
function clean_localStr(){
localStorage.clear();
}
ここまでくればあと少し。
html,body{
margin:0px;
flex: 0;
overflow:hidden;
}
#window_panel{
position: absolute;
flex: 0;
background:#999;
}
#input_panel{
position: absolute;
flex: 0;
background:rgb(73, 72, 72);
}
#headder_panel{
position:fixed;
background:rgb(73, 72, 72);
}
#timeline_panel{
position: absolute;
background:#EEE;
}
#btn_clean{
position:absolute;
display: inline-block;
background: rgb(73, 72, 72);
color: rgb(73, 72, 72);
text-align: center;
vertical-align: middle;
overflow: hidden;
}
#btn_clean:active {
border-bottom: solid 1px rgb(73, 72, 75);;
box-shadow: 0 0 0px rgba(0, 0, 0, 0.40);
border-radius: 10%;
}
#btn_clean:btn_cln {
position: absolute;
}
#btn_sample{
position:absolute;
display: inline-block;
background: rgb(73, 72, 72);
color: rgb(73, 72, 72);
border-radius: 50%;
text-align: center;
vertical-align: middle;
overflow: hidden;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.29);
text-shadow: -0.2px -0.2px rgba(255, 255, 255, 0.43), 1px 1px rgba(0, 0, 0, 0.49);
}
#btn_sample:active {
border-bottom: solid 1px #6d6c6d;
box-shadow: 0 0 1px rgba(0, 0, 0, 0.40);
border-radius: 50%;
}
#btn_sanmple:btn_pic{
position: absolute;
}
#textInput_area{
position: absolute;
font-size: 100%;
background:#EEE;
overflow:hidden;
border:none;
resize: none;
}
#message_container{
position:absolute;
background:#000;
}
#message_0{
position:absolute;
background:#CCC;
font-size: 100%;
}
左がSafari右がChrome ブラウザです。
是非やって見てください。
※ 何度もいいますが丸ボタン画像とゴミ箱画像用意してくださいね。
操作方法はHTMLあたりのコードを見てください。
参考にした記事はこちら
http://www.tettori.net/post/852/
##最後に
記事書くのって結構労力必要ですね。
JS初めて4ヶ月そこらなので
どうしてこうなのとか、ここはこうした方がいい等
是非コメントしていただけると嬉しいです。