スタジオ管理シートアプリの制作② ~エスケープ処理・Sass・CSS設計~

はじめに

前回、以下のアプリを作りました。

サークルの幹事メンバーに使ってもらったところ不具合がさっそく出たので修正します。笑

※過去記事にぼちぼち追記していたのですが、ボリューミー過ぎたので分けます。

スタジオ管理シートアプリの制作

指摘いただいた点

①ボタンが押しづらい。大きく&感覚をあけてほしい。

②曲名と名前の改行がみづらい。

③表と見学でエスケープ処理

修正

①ボタンが押しづらい。大きく&感覚をあけてほしい。

リファレンスとして使ったGoogleFormとじぶんのサイト比較。

 

あきらかにサイズが違う!!笑

サイズを調べると違いすぎたので修正していきます。

GoogleForm 76.5×30

じぶんのサイト 20×15

ボタンの感覚も今は5px程度→15pxにします。

色味もグーグルフォームを真似てみた。

🙄色味を濃くすると白文字が際立つ。boldも抜いても良いかも。

ついでにヘッダーのボタンはログイン・ログアウトボタンをfloat:rightしました。

②曲名と名前の改行がみづらい。

グーグルフォームよくみると、曲ごとにうっすら色味がついてることに気付く。

チェックボックスが真ん中行かないのが気になる。。。

それと見学、初参加のチェックボックスを左に持ってきたい。

.ul-body {
  // チェックボックスの背景をグレーに設定
  background-color: rgba(209, 209, 209, 0.129);
  padding: 5px 0;
  border-radius: 5px;
  li {
    font-size: 12px;
    padding: 5px;
  }
  // checkboxを中央寄せ
  input {
    margin:auto;
  }
  .single-checkbox {
    margin-right: 232px;
  }
}

かなり雑だけど一旦これで。(PC非対応)

続いて悲惨な状態と化したシート側へ。

名前の改行が見づらい点はドットマークを付けることで修正。

1人目の場合

// sheet_elem.textContent = firebase_db_sheet.name;
sheet_elem.textContent = `・${firebase_db_sheet.name}`;

2人目以降

// sheet_elem.innerHTML = `${sheet_elem.innerHTML}<br>${firebase_db_sheet.name}`;
sheet_elem.innerHTML = `${sheet_elem.innerHTML}<br>・${firebase_db_sheet.name}`;

前より良さげ。

また、人数増えることで起きる型崩れを直していきます。

とりあえずテーブルは色付けと太字でメリハリ付けます。

横幅を揃えたい。以下を加えたら均等になった〇

table-layout: fixed;

③表と見学でエスケープ処理

幹事陣に突っ込まれたエスケープ処理の指摘。。。笑

かなしい。。。笑

 

原因は以下の部分。名前がない場合はtextContentで、名前があった場合は改行して追加したかったのでinnerHTMLを使っているため。

sheet.js

let sheet_elem = document.getElementById(`sheet_${firebase_db_sheet.id}`);
// ifまだ名前がシートにない場合elseある場合
if (sheet_elem.textContent != ""){
  sheet_elem.innerHTML = `${sheet_elem.innerHTML}<br>・${firebase_db_sheet.name}`;
} else {
  sheet_elem.textContent = firebase_db_sheet.name;
}

本格入門(書籍)でもtextContentを推奨されていて、セキュリティ上の問題を本来は配慮しなければいけない。

ブラウザーで不特定多数のユーザーから勝手に意図しないコードが実行されてしまう可能性をXSS脆弱性という。これに該当しますね。。。

2つ以上あるとHTMLタグが処理される

・改行

任意の場所で改行できていないのでここも変えていきます。

調べたところ全角4文字まで、半角6文字まで。

なぜか半角は改行されない!!!

参考:テーブル内で長い文字列を決まった長さで改行させる方法

以下で改行できた。

word-wrap:break-word;

ただ黒点に続かず改行される。。。

まあ英語で入力されることはないってことでステイでもいいか。。。

参考:HTML文字列をエスケープする

ここでは特殊な記号に対するエスケープ処理を関数宣言して対処。

function escapeSpecialChars(str) {
  return str
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

また、テンプレートリテラルにタグづけするためのタグ関数を定義。

値が文字列型であればエスケープ。

function escapeHTML(strings, ...values) {
  return strings.reduce((result, str, i) => {
    const value = values[i - 1];
    if (typeof value === "string") {
      return result + escapeSpecialChars(value) + str;
    } else {
      return result + String(value) + str;
    }
  }); 
}

ここまで来てはてなマーク。。。難しい。

とりあえず今回はinnerHTMLを使わなければいいので、appendChildで実装してみることにしました。

また、改行はpタグ使えば自動で改行されるので、tableタグ内にpタグを1つずつ埋め込みます。

こんな感じ。

table p {
  margin: 0;
  font-weight: lighter;
}

上述したinnerHTMLとtextContentが混在していた処理を以下に。

firebase_db_sheet.id.map((id) => {
  let sheet_elem = document.getElementById(`sheet_${id}`);
    let mkText = document.createElement('p');
    mkText.textContent = `・${firebase_db_sheet.name}`;
    sheet_elem.appendChild(mkText);
});

🙄むしろスッキリするという。。。笑

さらに!!

// 1つ=配列じゃなかった場合
} else {
  let sheet_elem = document.getElementById(`sheet_${firebase_db_sheet.id}`);
  // ifまだ名前がシートにない場合elseある場合
  console.log("mkText2")
  let mkText = document.createElement('p');
  mkText.textContent = `・${firebase_db_sheet.name}`;
  sheet_elem.appendChild(mkText);
}

上記のようにidが配列じゃなかった場合の処理も書いていましたが、このケース、ないことが発覚したので消しました。。。笑

一件落着◎

スマホから見てもまあ悪くはない。

 

リポジトリ

CSSのリファクタリング

現在学習中のCSS設計(OOCSS/BEM)の練習もせっかくなのでしてみます。

CSS設計(SMACSS/BEMなど)まとめ

①マルチクラス設計

マルチクラス設計を意識したらだいぶすっきりした。

※以前はすべてのクラスに対してbackground-colorとか書いてた。

/*
  レイアウトグループ
*/

.ly {
  background-color: white;
  border-radius: 5px;
  padding: 5px;
  margin-bottom: 10px;
}

.ly-title {
  font-size: 22px;
}

.ly-main {
  margin-bottom: 30px; // スマホで下のボタン押しやすくする用
}

②HTMLの要素を使わない

pug側

select#rootSelect.display-none

js側

root_select.className = "display-inline name-select";

JSでdisplay-noneからのクラス付与しているケースが多いから要注意。あまりよくない気も。。。

③クラス名の指す場所がわかりづらいクラスの修正

以下、どこを指しているか自分でも全く分からない。。。

.body

修正。

.ul-body

🙄見返して我ながら最低なクラス命名だと思った。

Sassを活用する

参考:Web制作者のためのSassの教科書 

①ネストする

CSS

.ul-head > li {
  font-weight: bold;
}

.ul-head > .li-category {
  width: 35px;
}

Scss

.ul-head {
  > li {
    font-weight: bold;
  }
  > .li-category {
    width: 35px;
  }
}

🙄シンプルにわかりやすい。

②メディアクエリのネスト

CSS

body {
  color: black;
  background-color: rgba(255, 195, 104, 0.313);
  margin: 0 auto;
  font-family: sans-serif;
  max-width: 90%;
}

@media screen and (min-width:768px) and ( max-width:1024px) {
  body {
    max-width: 40%;
  }
}

@media screen and (min-width:1024px) {
  body {
    max-width: 30%;
  }
}

Scss

※更にメディアクエリを1つ追加しています

body {
  color: black;
  background-color: rgba(255, 195, 104, 0.313);
  margin: 0 auto;
  font-family: sans-serif;
  max-width: 90%;
  @media screen and (min-width: 500px) {
    max-width: 60%;
  }
  @media screen and (min-width: 700px) {
    max-width: 40%;
  }
  @media screen and (min-width: 1024px) {
    max-width: 30%;
  }
}

リファクタリング

sheet.jsのこの部分を編集

※ルート、ログイン、未ログインでほぼ同じ内容を3回繰り返していたので。。。

// ログインユーザー
db.ref(`/users/login`).on('child_added', (data) => {
  console.log("mksheet_login");
  firebase_mk_select.push(data.val());
  firebase_db_sheet = data.val();
  firebase_name_all.push(data.val());
  if (firebase_db_sheet != null) {
    if (Array.isArray(firebase_db_sheet.id) === true){
      firebase_db_sheet.id.map((id) => {
        let sheet_elem = document.getElementById(`sheet_${id}`);
          console.log("mkText")
          let mkText = document.createElement('p');
          mkText.textContent = `・${firebase_db_sheet.name}`;
          sheet_elem.appendChild(mkText);
      });
    }
  }
});

以下のように関数にして抽出。

db.ref(`/users/login`).on('child_added', (data) => {
  console.log("mksheet_login");
  firebase_mk_select.push(data.val());
  mkSheetChild(data);
});
let mkSheetChild = (data) => {
  firebase_db_sheet = data.val();
  firebase_name_all.push(data.val());
  if (firebase_db_sheet != null) {
    if (Array.isArray(firebase_db_sheet.id) === true) {
      firebase_db_sheet.id.map((id) => {
        let sheet_elem = document.getElementById(`sheet_${id}`);
        console.log("mkText");
        let mkText = document.createElement('p');
        mkText.textContent = `・${firebase_db_sheet.name}`;
        sheet_elem.appendChild(mkText);
      });
    }
  }
}

だいぶすっきりしました◎

今回の自分の命名規則メモ

①classはCSS、idはJS専用

②HTML上でidはキャメルケース(2つ目以降の単語を大文字)、JSでHTMLのidタグの要素を代入した変数はスネークケース(_)

③CSSは基本ケバブケース(-)、例外にBEM(__と–)。

→BEMで命名規則をしっかりさせれば、CSSのクラスを取得してJSの操作をしてもいいのかもしれない。

おわりに

今回、デザインに関しては知識が皆無であれば、見よう見まねでまずはパクるのが大事!と感じた。

1つ1つデザインの意図がすこしはわかるようになるから勉強になると思った。

コーディングで模写が大事って言われている意味が分かった気がする。

今回やっとSassを使ったがCSS設計を学んで試し始めると不思議なことにとても自然にSassを使いたくなった。

また、OOCSSは素のJSを勉強し始めたからこそ理解できる部分も多かった。

少しずついろんなことが繋がり始めているので、この調子で引き続き勉強頑張ろう!

備忘録

✅デモ用の実装まとめ(複製して別途作成しています)

①init.jsのfirebaseConnected関数内に追記

// 接続後の処理まとめ
let firebaseConnected = () => {
  databaseInitFC();
  setTimeout(()=> {
    if (firebase.auth().currentUser != null && firebase.auth().currentUser.displayName === null){
      console.log("rootFC")
      rootFC();
      test_login_btn.className = "display-none";
      user_name.value = "幹事(テストユーザー)"
      root_uid.push(firebase.auth().currentUser.uid);
    }
  },2000);
};

🙄displayNameがテストログインだとnullになる仕様微妙すぎる。。。

②auth.jsのloggedInFC関数内に追記

ログイン後、テストログイン押しても、ユーザーは上書きされないようなのでログインとテストログインは同時に表示されないように。

// 通常ログインの場合テストログインのボタンを非表示
if (firebase.auth().currentUser != null && firebase.auth().currentUser.displayName != null){
  test_login_btn.className = "display-none";
}

※PCからだとログインできない現象がまれに起きた・・・あとチェックもリロードすると付かない現象が起きてる。。。ここあたりは一旦ステイします。。。

③テストログイン機能をauth.jsに追記

// テスト幹事ログイン
let testLoginFC = () => {
  firebase.auth().signInAnonymously()
  .catch(function(error) {
    console.log(error.message);
  });
  firebase.auth().onAuthStateChanged(function(user) {
    var isAnonymous = user.isAnonymous;
    var uid = user.uid;
    location.reload();
  });
};

③pugの追記部分

button#testLoginBtn.display-inline(onclick="testRootLoginFC()") テストログイン(幹事)

p.supplement-message#supplement-message ※幹事機能はデモ用です。通常のログインでは、自身の登録情報が保持(編集・削除可能)されますが、テストログイン時は保持されません

④Sass

.test-login-btn {
  width: 140px;
}

 

コメントを残す