複数画像投稿機能+プレビュー機能
学習中、コードが冗長になってきたということでコードが整理されました。
整理前
document.addEventListener('DOMContentLoaded', function(){
// 新規投稿・編集ページのフォームを取得
const postForm = document.getElementById('new_post');
// プレビューを表示するためのスペースを取得
const previewList = document.getElementById('previews');
// 新規投稿・編集ページのフォームがないならここで終了。「!」は論理否定演算子。
if (!postForm) return null;
// input要素を取得
const fileField = document.querySelector('input[type="file"][name="post[images][]"]');
// input要素で値の変化が起きた際に呼び出される関数
fileField.addEventListener('change', function(e){
// data-index(何番目を操作しているか)を取得
const dataIndex = e.target.getAttribute('data-index');
// 古いプレビューが存在する場合は削除
const alreadyPreview = document.querySelector('.preview');
if (alreadyPreview) {
alreadyPreview.remove();
};
const file = e.target.files[0];
const blob = window.URL.createObjectURL(file);
// 画像を表示するためのdiv要素を生成
const previewWrapper = document.createElement('div');
previewWrapper.setAttribute('class', 'preview');
previewWrapper.setAttribute('data-index', dataIndex);
// 表示する画像を生成
const previewImage= document.createElement('img');
previewImage.setAttribute('class', 'preview-image');
previewImage.setAttribute('src', blob);
// 生成したHTMLの要素をブラウザに表示させる
previewWrapper.appendChild(previewImage);
previewList.appendChild(previewWrapper);
// 2枚目用のfile_fieldを作成
const newFileField = document.createElement('input');
newFileField.setAttribute('type', 'file');
newFileField.setAttribute('name', 'post[images][]');
// 最後のfile_fieldを取得
const lastFileField = document.querySelector('input[type="file"][name="post[images][]"]:last-child');
// nextDataIndex = 最後のfile_fieldのdata-index + 1
const nextDataIndex = Number(lastFileField.getAttribute('data-index')) +1;
newFileField.setAttribute('data-index', nextDataIndex);
// 生成したfile_fieldを表示
const fileFieldsArea = document.querySelector('.click-upload');
fileFieldsArea.appendChild(newFileField);
});
});
整理後
document.addEventListener('DOMContentLoaded', function(){
// 新規投稿・編集ページのフォームを取得
const postForm = document.getElementById('new_post');
// プレビューを表示するためのスペースを取得
const previewList = document.getElementById('previews');
// 新規投稿・編集ページのフォームがないならここで終了。「!」は論理否定演算子。
if (!postForm) return null;
// プレビュー画像を生成・表示する関数
const buildPreviewImage = (dataIndex, blob) =>{
// 画像を表示するためのdiv要素を生成
const previewWrapper = document.createElement('div');
previewWrapper.setAttribute('class', 'preview');
previewWrapper.setAttribute('data-index', dataIndex);
// 表示する画像を生成
const previewImage= document.createElement('img');
previewImage.setAttribute('class', 'preview-image');
previewImage.setAttribute('src', blob);
// 生成したHTMLの要素をブラウザに表示させる
previewWrapper.appendChild(previewImage);
previewList.appendChild(previewWrapper);
};
// file_fieldを生成・表示する関数
const buildNewFileField = () => {
// 2枚目用のfile_fieldを作成
const newFileField = document.createElement('input');
newFileField.setAttribute('type', 'file');
newFileField.setAttribute('name', 'post[images][]');
// 最後のfile_fieldを取得
const lastFileField = document.querySelector('input[type="file"][name="post[images][]"]:last-child');
// nextDataIndex = 最後のfile_fieldのdata-index + 1
const nextDataIndex = Number(lastFileField.getAttribute('data-index')) +1;
newFileField.setAttribute('data-index', nextDataIndex);
// 生成したfile_fieldを表示
const fileFieldsArea = document.querySelector('.click-upload');
fileFieldsArea.appendChild(newFileField);
};
// input要素で値の変化が起きた際に呼び出される関数の中身
const changedFileField = (e) => {
// data-index(何番目を操作しているか)を取得
const dataIndex = e.target.getAttribute('data-index');
// 古いプレビューが存在する場合は削除
const alreadyPreview = document.querySelector('.preview');
if (alreadyPreview) {
alreadyPreview.remove();
};
const file = e.target.files[0];
const blob = window.URL.createObjectURL(file);
buildPreviewImage(dataIndex, blob);
buildNewFileField();
};
// input要素を取得
const fileField = document.querySelector('input[type="file"][name="post[images][]"]');
// input要素で値の変化が起きた際に呼び出される関数
fileField.addEventListener('change', changedFileField);
});
機能ごとに関数を用いて処理を切り分け、整理されているところまでは分かりました。
1. プレビュー画像を生成・表示する関数 = buildPreviewImage でまとめる。
2. file_fieldを生成・表示する関数 = buildNewFileField でまとめる。
3. input要素で値の変化が起きた際に呼び出される関数の中身 = changedFileField でまとめる。
4. 関数でまとめた為、1,2,3を「input要素で値の変化が起きた際に呼び出される関数」の上に関数の定義を配置する。
疑問なのは、この機能がまだ未完成の状態で、なぜこのタイミングでコードを整理を行ったのか?
仮説としては、このタイミングで機能が出揃ったため整理できるとした。
だとしたら、完成形をある程度予想しておく必要がある。
実装の手順としては、
1. 実装のための機能の洗い出し
2. ある程度のコードを想定する
3. 実際に着手する
4. 必要がある際は修正する
という感じでしょうか。
中々難しいものです。。。