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

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

新規登録して質問してみよう
ただいま回答率
85.35%
JavaScript

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

2回答

1625閲覧

JavaScriptで生成したランダムなコードを、日付が変わるまで維持する方法を教えてください

numin

総合スコア30

JavaScript

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

1クリップ

投稿2021/12/03 17:27

#JavaScriptで生成したランダムなコードが、日付が変わるまで維持される方法を教えてください。
##実現したいこと
利用者が名前を入力・登録するとアルファベット1文字+数字2桁のゆーざコードが作成される(重複あり)というページを作っているのですが、ランダムなコードを生成することはできてもそれを日付が変わるまで維持させる方法がわかりません。現状、ページを更新する度にユーザコードが変わってしまい、その後他のページで利用することができません。
一度生成されたコードはその日のうちは変わることなく、日付が変われば次のコードが表示される、という仕組みしたいのですが、どのように記述すれば宜しいのでしょうか。
##コード
'use strict';

// 配列と乱数を定義
const codeArea = document.getElementById('code-area');
const alphabet = ["A", "C", "F", "N", "S"];
const num1 = Math.floor(Math.random() * (alphabet.length - 1));
const score = ["11", "12", "13", "14", "21", "22", "23", "24", "25", "31", "32", "33", "34"];
const num2 = Math.floor(Math.random() * (score.length - 1));

// ページ読み込み時の処理
window.onload = function() {
// 名前が登録されていれば当日のコードが表示される。
if(localStorage.getItem('savedName')) {
const name = localStorage.getItem('savedName');
codeArea.innerHTML = <p>${name}さんの今日のユーザコードは${alphabet[num1]}${score[num2]}です。;
} else {
codeArea.innerHTML = '<p>名前を入力して今日のユーザコードを取得してください。</p><p><input type="text" id="your-name" size="30" value="山田太郎"> <button type="button" id="input-name" onclick="saveName();">登録 ▶</button></p>';
}
}

function saveName() {
let yourName = document.getElementById('your-name').value;
localStorage.setItem('savedName', yourName);
document.location.reload();
}

宜しくお願い致します。

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

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

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

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

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

guest

回答2

0

ベストアンサー

修正案の一例として、回答します。要点としては以下の2点です。

(1) localStorage に保存する情報を追加

localStorage に保存する情報に、入力された名前に加えて、以下の2点を追加します。

  • 生成されたユーザーコード
  • 上記のユーザーコードの有効期限を表す文字列

そこで、localStorage には以下のキーと値をsetItem します。

  • キー: yourInfo
  • 値: 以下のようなJSON文字列

json

1{"name":"山田太郎","code":"A11","expireDate":"2021-12-04 23:59:59"}

expireDate は、ユーザーコードが生成された日時の時刻部分を、その日の最終時刻を表す23:59:59 に変えたものです。

(2) 日時の操作にDayjsを利用する。

表示の際に、localStorage に yourInfoキーで上記のJSONが保存されていればparseして、現在日時とexpireDateを比較することになります。この際に、日時の操作で便利なライブラリDayjsを使います。Dayjsを使えば、「ある時点の当日最終日時」といった日時も容易に作ることができます。

以下は、上記(1)、(2)による修正コードの例です。

index.html

html

1<!DOCTYPE html> 2<html lang="ja"> 3<head> 4 <meta charset="UTF-8"> 5 <title>tera: 372186</title> 6 <script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.10.7/dayjs.min.js" 7 integrity="sha512-bwD3VD/j6ypSSnyjuaURidZksoVx3L1RPvTkleC48SbHCZsemT3VKMD39KknPnH728LLXVMTisESIBOAb5/W0Q==" 8 crossorigin="anonymous" 9 referrerpolicy="no-referrer"></script> 10 <script src="script.js"></script> 11</head> 12<body> 13<div id="code-area">code-area</div> 14</body> 15</html> 16

script.js

javascript

1'use strict'; 2 3const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss'; 4 5// ページ読み込み時の処理 6window.onload = function () { 7 const codeArea = document.getElementById('code-area'); 8 const inputHtml = `<p>名前を入力して今日のユーザコードを取得してください。</p> 9 <p> 10 <input type="text" id="your-name" size="30" value="山田太郎"> 11 <button type="button" id="input-name" onclick="saveUserInfo();">登録 ▶</button> 12 </p>`; 13 14 const yourInfoJson = localStorage.getItem('yourInfo'); 15 16 if (yourInfoJson) { 17 const { name, code, expireDate } = JSON.parse(yourInfoJson); 18 if (dayjs().isBefore(dayjs(expireDate, DATE_FORMAT))){ 19 codeArea.innerHTML = `<p>${name}さんの今日のユーザコードは${code}です。`; 20 } else { 21 localStorage.removeItem('yourInfo'); 22 codeArea.innerHTML = inputHtml; 23 } 24 } else { 25 codeArea.innerHTML = inputHtml; 26 } 27} 28 29// ユーザーコード作成関数 30function makeUserCode() { 31 const randomChoice = ary => ary[Math.floor(Math.random() * ary.length)]; 32 const alphabet = ["A", "C", "F", "N", "S"]; 33 const score = ["11", "12", "13", "14", "21", "22", "23", "24", "25", "31", "32", "33", "34"]; 34 return [alphabet, score].map(randomChoice).join(''); 35} 36 37// ユーザー情報保存関数 38function saveUserInfo() { 39 const name = document.getElementById('your-name').value; 40 const code = makeUserCode(); 41 const expireDate = dayjs().endOf('day').format(DATE_FORMAT); 42 const userInfo = { name, code, expireDate }; 43 44 localStorage.setItem('yourInfo', JSON.stringify(userInfo)); 45 document.location.reload(); 46} 47

上記のコードでは、Dayjsのメソッドとして、

  • 二つの日時の比較を行うためのisBefore
  • ある日の最終日時を作るためにendOf

の2点を使っています。

追記

上記のコードで、expireDateを作るのに

javascript

1const expireDate = dayjs().endOf('day').format(DATE_FORMAT);

としていましたが、見直してみると、以下にするほうがより正確でした。

javascript

1const expireDate = dayjs().add(1, 'day').startOf('day').format(DATE_FORMAT);

これで、expireDate にはユーザーコードが生成された時点の翌日0時0分0秒に設定されるので、

javascript

1if (dayjs().isBefore(dayjs(expireDate, DATE_FORMAT))){

によって現在日時と比較される日時として、より妥当なものになります。

追記2

上記までのコードだと、localStorageに保存されたyourInfoのexpireDateを改ざんすることで、有効期限を故意に伸ばすことができてしまうので暗号化します。暗号化には、crypto-js を使います。

index.html

diff

1 crossorigin="anonymous" 2 referrerpolicy="no-referrer"></script> 3+ <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script> 4 <script src="script.js"></script> 5 </head> 6 <body>
script.js

diff

1 if (yourInfoJson) { 2 const { name, code, expireDate } = JSON.parse(yourInfoJson); 3- if (dayjs().isBefore(dayjs(expireDate, DATE_FORMAT))){ 4+ const decryptedExpireDate = CryptoJS.AES.decrypt(expireDate, `${name}${code}`).toString(CryptoJS.enc.Utf8); 5+ if (dayjs().isBefore(dayjs(decryptedExpireDate, DATE_FORMAT))){ 6 codeArea.innerHTML = `<p>${name}さんの今日のユーザコードは${code}です。`; 7 } else { 8 localStorage.removeItem('yourInfo');

diff

1 const name = document.getElementById('your-name').value; 2 const code = makeUserCode(); 3 const expireDate = dayjs().add(1, 'day').startOf('day').format(DATE_FORMAT); 4- const userInfo = { name, code, expireDate }; 5+ const encryptedExpireDate = CryptoJS.AES.encrypt(expireDate,`${name}${code}`).toString(); 6+ const userInfo = { name, code, expireDate: encryptedExpireDate }; 7 localStorage.setItem('yourInfo', JSON.stringify(userInfo)); 8 document.location.reload(); 9 }

しかし、これでも script.js が簡単に読めてしまっては、プログラムが読める人なら、expireDateの暗号化に、crypto-jsを使っていて、暗号化と復号化のカギに ${name}${code} を使っていることが読み取れてしまいますので、 復号されたexpireDateが 2050-01-01 00:00:00 になるように、localStorageを書き換えてしまうかもしれません。これを防ぐために script.jsを難読化します。
たとえばJavaScript Obfuscator Tool というオンラインの難読化ツールを使うと、script.js が以下のように難読化されます。

javascript

1'use strict';function _0x1d81(_0x6942b4,_0x34924a){const _0x1e71e1=_0x1e71();return _0x1d81=function(_0x1d8116,_0x127fa8){_0x1d8116=_0x1d8116-0x82;let _0x1f92bb=_0x1e71e1 ・・・以下省略

(※回答文字数の上限を超えるため、省略しました)

これでも万全ではありませんから、より安全にするには入力された名前からのユーザーコードの生成とそのコードの有効期限の管理を、サーバーサイドで行い、画面側でそのコードを使うときはサーバーサイドへそのコードがまだ使えるかの問い合わせをすることが考えられます。

追記3

コメントから頂きました

今回のコードで名前の入力を2度目以降省略したい時には、

との件について回答します。ご提示の

”name”を単独でlocalStorageに保存した後、”if (yourInfoJson)”の外側に追加でif文を記載すれば宜しいのでしょうか。

という修正でも出来るとは思いますが、localStorageに保存する情報はこれまでに回答したyourInfoキーで保存する以下の形式のJSONオブジェクト

json

1{"name":"山田太郎","code":"N23","expireDate":"U2FsdGVkX1+Nm9QtGzUMtF8UaJXYYtIQXAT4ODxj6o8p5o9skBrOqnGoQ0vji5Hw"}

のみでも対応は可能かと思います。たとえば、追記2に記載したexpireDateを暗号化する修正後のコードに対して、さらに以下の**(1)(3)**のような修正を行います。

(1) const inputHtml を固定のHTMLから、HTMLを返す関数にする。

inputHtmlを修正して、名前を引数nameで受け取り、それが

  • undefined(などfalsy)のときには名前を入力できるtype="text"のinputとし
  • 一文字以上の文字列のときは、type="hidden" のinputとする

ようなHTMLを返す関数にします。これは以下のような修正です。

diff

1 window.onload = function () { 2 const codeArea = document.getElementById('code-area'); 3- const inputHtml = `<p>名前を入力して今日のユーザコードを取得してください。</p> 4+ const inputHtml = name => `<p>${name ? '' : '名前を入力して'}今日のユーザコードを取得してください。</p> 5 <p> 6- <input type="text" id="your-name" size="30" value="山田太郎"> 7+ <input type="${name ? 'hidden' : 'text'}" id="your-name" size="30" value="${name || '山田太郎'}"> 8 <button type="button" id="input-name" onclick="saveUserInfo();">登録 ▶</button> 9 </p>`;
(2) 上記の(1)で関数にした inputHtml を使う箇所の修正

以下のように修正します。

  • キーyourInfo で localStorage にJSONが保存されていた場合:

現在日時が(暗号から戻された)expireDate以降となっている、または、localStorageに暗号化されたexpireDateが改ざんされるなどして、decryptedExpireDateが日付として不正となっている場合に、
ⅰ) localStorage から yourInfo をremoveItem()で消していたが、消さないでおく
ⅱ) codeArea.innerHTML に inputHtml(name) が返す、(inputのtypeがhiddenになっている)HTMLを入れる。

  • キーyourInfo で localStorage にJSONが保存されていなかった場合:

  ⅲ) codeArea.innerHTML に inputHtml() が返す、(inputのtypeがtextになっている)HTMLを入れる。

diff

1 if (dayjs().isBefore(dayjs(decryptedExpireDate, DATE_FORMAT))){ 2 codeArea.innerHTML = `<p>${name}さんの今日のユーザコードは${code}です。`; 3 } else { 4- localStorage.removeItem('yourInfo'); 5- codeArea.innerHTML = inputHtml; 6+ codeArea.innerHTML = inputHtml(name); 7 } 8 } else { 9- codeArea.innerHTML = inputHtml; 10+ codeArea.innerHTML = inputHtml(); 11 }
(3) 暗号化されたexpireDateの復号時に例外が発生した場合の対応

さらに、

今回のコードで名前の入力を2度目以降省略したい時には、

の件とは関係ありませんが、localStorageに保存されている暗号化されたexpireDateを、ユーザーが改ざんした場合に、

javascript

1const decryptedExpireDate = CryptoJS.AES.decrypt(expireDate, `${name}${code}`).toString(CryptoJS.enc.Utf8);

の部分で例外が発生する場合があるので、これを catch しておきます。

diff

1 if (yourInfoJson) { 2 const { name, code, expireDate } = JSON.parse(yourInfoJson); 3- const decryptedExpireDate = CryptoJS.AES.decrypt(expireDate, `${name}${code}`).toString(CryptoJS.enc.Utf8); 4+ let decryptedExpireDate = null; 5+ try { 6+ decryptedExpireDate = CryptoJS.AES.decrypt(expireDate, `${name}${code}`).toString(CryptoJS.enc.Utf8); 7+ } catch (e) { 8+ console.error(e); 9+ } 10 if (dayjs().isBefore(dayjs(decryptedExpireDate, DATE_FORMAT))){ 11 codeArea.innerHTML = `<p>${name}さんの今日のユーザコードは${code}です。`; 12

この修正で、例外が発生した場合、decryptedExpireDatenull になりますが、その場合も

javascript

1dayjs(decryptedExpireDate, DATE_FORMAT)

はエラーにならず、日時として不正な内容を含むdayjsオブジェクトになり、それを比較対象にした isBefore 判定は(期待どおり) false を返してくれます。

以上の修正**(1)(3)**によって、意図した動作のものになるかと思います。

投稿2021/12/04 03:47

編集2021/12/07 15:58
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

numin

2021/12/06 17:20

ご丁寧に説明して頂きありがとうございます。dayjsなんて便利なライブラリがあったんですね。JavaScriptの難読化についても、非常に勉強になりました。 重ねて質問をしてしまい申し訳ありませんが、今回のコードで名前の入力を2度目以降省略したい時には、”name”を単独でlocalStorageに保存した後、”if (yourInfoJson)”の外側に追加でif文を記載すれば宜しいのでしょうか。 教えて頂けると幸いです。
退会済みユーザー

退会済みユーザー

2021/12/07 15:55

コメントありがとうございます。ご質問の件、回答のほうに追記3として回答しました。
guest

0

日付が変わるまで維持させる方法

「ユーザ名+yyyymmdd」の文字列からハッシュ値を求め、その値に応じてユーザーコードを取得するというのは如何でしょうか?
以下、サンプルコードになります。同じ日であれば同じユーザーコードを返すことは確認しました。

javascript

1function getYYYYMMDD(dt) { 2 const yy = dt.getFullYear(); 3 const mm = ('00' + (dt.getMonth()+1)).slice(-2); 4 const dd = ('00' + dt.getDate()).slice(-2); 5 return (yy + mm + dd); 6} 7 8function getHashCodeByName(name) { 9 var hash = 0; 10 if (name.length == 0) { 11 return hash; 12 } 13 for (let i = 0; i < name.length; i++) { 14 const c = name.charCodeAt(i); 15 hash = ((hash<<5)-hash)+c; 16 hash = hash & hash; 17 } 18 return Math.abs(hash); 19} 20 21function getUserCodeByUserName(userName) { 22 // 配列と乱数を定義 23 const hashCode = getHashCodeByName(userName); 24 const alphabet = ["A", "C", "F", "N", "S"]; 25 const num1 = hashCode % alphabet.length; 26 const score = ["11", "12", "13", "14", "21", "22", "23", "24", "25", "31", "32", "33", "34"]; 27 const num2 = hashCode % score.length; 28 const userCode = alphabet[num1] + score[num2]; 29 return userCode; 30} 31 32// テスト 33console.log("helloさんの 今日 のユーザーコード:"+getUserCodeByUserName("hello"+getYYYYMMDD(new Date()))); 34console.log("helloさんの 今日 のユーザーコード:"+getUserCodeByUserName("hello"+getYYYYMMDD(new Date()))); 35 36console.log("abcさんの 2021/12/04 のユーザーコード:"+getUserCodeByUserName("abc20211204")); 37console.log("abcさんの 2021/12/05 のユーザーコード:"+getUserCodeByUserName("abc20211205")); 38console.log("abcさんの 2021/12/06 のユーザーコード:"+getUserCodeByUserName("abc20211206")); 39console.log("abcさんの 2021/12/04 のユーザーコード:"+getUserCodeByUserName("abc20211204")); 40 41console.log("defさんの 2021/12/04 のユーザーコード:"+getUserCodeByUserName("def20211204")); 42console.log("defさんの 2021/12/05 のユーザーコード:"+getUserCodeByUserName("def20211205")); 43console.log("defさんの 2021/12/06 のユーザーコード:"+getUserCodeByUserName("def20211206")); 44console.log("defさんの 2021/12/04 のユーザーコード:"+getUserCodeByUserName("def20211204")); 45

■ テスト結果

text

1helloさんの 今日 のユーザーコード:N21 2helloさんの 今日 のユーザーコード:N21 3 4abcさんの 2021/12/04 のユーザーコード:N24 5abcさんの 2021/12/05 のユーザーコード:F23 6abcさんの 2021/12/06 のユーザーコード:C22 7abcさんの 2021/12/04 のユーザーコード:N24 8 9defさんの 2021/12/04 のユーザーコード:S14 10defさんの 2021/12/05 のユーザーコード:A21 11defさんの 2021/12/06 のユーザーコード:C22 12defさんの 2021/12/04 のユーザーコード:S14

<参考>
■ 日付をYYYY-MM-DDの書式で返す関数 (JavaScript)
https://qiita.com/toshimin/items/5f13c3b4c28825219231
■ JavaScript用の単純な(非セキュア)ハッシュ関数?
https://www.webdevqa.jp.net/ja/javascript/javascript%E7%94%A8%E3%81%AE%E5%8D%98%E7%B4%94%E3%81%AA%EF%BC%88%E9%9D%9E%E3%82%BB%E3%82%AD%E3%83%A5%E3%82%A2%EF%BC%89%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E9%96%A2%E6%95%B0%EF%BC%9F/972873955/
■ 剰余 (%) - JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Remainder

投稿2021/12/03 19:49

cx20

総合スコア4648

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問