こんにちは。KOUKIです。
本記事は、Udemyの「50 Projects In 50 Days – HTML, CSS & JavaScript」で学習したことを載せています。
<目次>
実装するもの
今回は、Testimonial Boxを作りましょう。ちなみに、Testimonialとは「お客様の声」と言う意味です。
demoは「こちら」で確認できます。
環境構築
簡単な環境構築をお願いします。
必要なファイルは、以下の通りです。
1 2 3 4 5 6 |
$ tree . ├── index.html ├── script.js └── style.css |
CSS版
ページ(HTML)の作成
最初にページを作成しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog==" crossorigin="anonymous" /> <link rel="stylesheet" href="style.css" /> <title>Testimonial Box</title> </head> <body> <div class="testimonial-container"> <div class="progress-bar"></div> <div class="fas fa-quote-right fa-quote"></div> <div class="fas fa-quote-left fa-quote"></div> <p class="testimonial"> I've worked with literally hundreds of HTML/CSS developers and I have to say the top spot goes to this guy. This guy is an amazing developer. He stresses on good, clean code and pays heed to the details. I love developers who respect each and every aspect of a throughly thought out design and do their best to put it in code. He goes over and beyond and transforms ART into PIXELS - without a glitch, every time. </p> <div class="user"> <img src="https://randomuser.me/api/portraits/women/46.jpg" alt="user" class="user-image" /> <div class="user-details"> <h4 class="username">Miyah Myles</h4> <p class="role">Marketing</p> </div> </div> </div> <script src="script.js"></script> </body> </html> |
このHTMLをブラウザ上で表示すると以下のようになります。

ユーザーの情報は、RANDOM USER GENERATORから取得しています。
スタイル(CSS)を装飾
次は、ページにスタイルをつけます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
@import url('https://fonts.googleapis.com/css?family=Montserrat'); * { box-sizing: border-box; } body { background-color: #f4f4f4; font-family: 'Montserrat', sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; overflow: hidden; margin: 0; padding: 10px; } .testimonial-container { background-color: #476ce4; color: #fff; border-radius: 15px; margin: 20px auto; padding: 50px 80px; max-width: 768px; position: relative; } .fa-quote { color: rgba(255, 255, 255, 0.3); font-size: 28px; position: absolute; top: 70px; } .fa-quote-right { left: 40px; } .fa-quote-left { right: 40px; } .testimonial { line-height: 28px; text-align: justify; } .user { display: flex; align-items: center; justify-content: center; } .user .user-image { border-radius: 50%; height: 75px; width: 75px; object-fit: cover; } .user .user-details { margin-left: 10px; } .user .username { margin: 0; } .user .role { font-weight: normal; margin: 10px 0; } .progress-bar { background-color: #fff; height: 4px; width: 100%; animation: grow 10s linear infinite; transform-origin: left; } @keyframes grow { 0% { transform: scaleX(0); } } @media (max-width: 768px) { .testimonial-container { padding: 20px 30px; } .fa-quote { display: none; } } |
ここまで実装すると以下のようになります。

プログレスバーの進捗表示はCSSで制御しているため、この時点でバーが動きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.progress-bar { background-color: #fff; height: 4px; width: 100%; animation: grow 10s linear infinite; transform-origin: left; } @keyframes grow { 0% { transform: scaleX(0); } } |
infiniteを指定するとアニメーション(grow)を無限に繰り返すようです。linearは、アニメーションを開始から終了まで一定間隔で実行します。
「transform-origin: left」では、アニメーション開始点を左側に指定してます。
アニメーション(grow)では「transform: scaleX(0);」にて、プログレスバーを消します。そして、プログレスバーには「width: 100%;」を指定しているので、アニメーションでじわりじわりと表示されるようです。
JavaScriptの実装
要素を取得
最初に、画面操作に必要な要素を取得します。
1 2 3 4 5 6 |
// 要素の取得 const testimonialsContainer = document.querySelector('.testimonials-container') const testimonial = document.querySelector('.testimonial') const userImage = document.querySelector('.user-image') const username = document.querySelector('.username') const role = document.querySelector('.role') |
お客様の声を定義
お客様の声を定数として定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
// データの定義 const testimonials = [ { name: 'Miyah Myles', position: 'Marketing', photo: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=200&fit=max&s=707b9c33066bf8808c934c8ab394dff6', text: "I've worked with literally hundreds of HTML/CSS developers and I have to say the top spot goes to this guy. This guy is an amazing developer. He stresses on good, clean code and pays heed to the details. I love developers who respect each and every aspect of a throughly thought out design and do their best to put it in code. He goes over and beyond and transforms ART into PIXELS - without a glitch, every time.", }, { name: 'June Cha', position: 'Software Engineer', photo: 'https://randomuser.me/api/portraits/women/44.jpg', text: 'This guy is an amazing frontend developer that delivered the task exactly how we need it, do your self a favor and hire him, you will not be disappointed by the work delivered. He will go the extra mile to make sure that you are happy with your project. I will surely work again with him!', }, { name: 'Iida Niskanen', position: 'Data Entry', photo: 'https://randomuser.me/api/portraits/women/68.jpg', text: "This guy is a hard worker. Communication was also very good with him and he was very responsive all the time, something not easy to find in many freelancers. We'll definitely repeat with him.", }, { name: 'Renee Sims', position: 'Receptionist', photo: 'https://randomuser.me/api/portraits/women/65.jpg', text: "This guy does everything he can to get the job done and done right. This is the second time I've hired him, and I'll hire him again in the future.", }, { name: 'Jonathan Nunfiez', position: 'Graphic Designer', photo: 'https://randomuser.me/api/portraits/men/43.jpg', text: "I had my concerns that due to a tight deadline this project can't be done. But this guy proved me wrong not only he delivered an outstanding work but he managed to deliver 1 day prior to the deadline. And when I asked for some revisions he made them in MINUTES. I'm looking forward to work with him again and I totally recommend him. Thanks again!", }, { name: 'Sasha Ho', position: 'Accountant', photo: 'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?h=350&auto=compress&cs=tinysrgb', text: 'This guy is a top notch designer and front end developer. He communicates well, works fast and produces quality work. We have been lucky to work with him!', }, { name: 'Veeti Seppanen', position: 'Director', photo: 'https://randomuser.me/api/portraits/men/97.jpg', text: 'This guy is a young and talented IT professional, proactive and responsible, with a strong work ethic. He is very strong in PSD2HTML conversions and HTML/CSS technology. He is a quick learner, eager to learn new technologies. He is focused and has the good dynamics to achieve due dates and outstanding results.', }, ] |
APIから動的に取得することもできそうですが、処理が複雑になるので定数で定義しています。
アップデート処理
最後に、一定間隔ごとにお客様の声をアップデートする処理を実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// データのインデックス let idx = 1 // アップデート処理 function updateTestimonial() { // JavaScriptだと{}+プロパティ名を指定すると // それぞれの変数に値を格納してくれる // ただし、同名に限る const { name, position, photo, text } = testimonials[idx] // データ格納 testimonial.innerHTML = text userImage.src = photo username.innerHTML = name role.innerHTML = position // インクリメント idx++ if (idx > testimonials.length - 1) { idx = 0 } } // 10秒間隔でアップデート処理を実行 setInterval(updateTestimonial, 10000) |
setIntervalは、第一引数に指定した関数を、指定した間隔で実行してくれる便利メソッドです。
これで、完成です。
おわりに
個人的には、CSSのプログレスバーの実装が興味深かったですね。アニメーションを駆使すれば、JavaScriptを使わなくても動的な動きを実装できる。。奥が深いですね^^
スタイルを簡単に実装できるようになれば、アプリケーション開発も楽しくなると思うので、勉強していきたいです。
それでは、また!
JavaScriptまとめ
JavaScript ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
// 要素の取得 const testimonialsContainer = document.querySelector('.testimonials-container') const testimonial = document.querySelector('.testimonial') const userImage = document.querySelector('.user-image') const username = document.querySelector('.username') const role = document.querySelector('.role') // データの定義 const testimonials = [ { name: 'Miyah Myles', position: 'Marketing', photo: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=200&fit=max&s=707b9c33066bf8808c934c8ab394dff6', text: "I've worked with literally hundreds of HTML/CSS developers and I have to say the top spot goes to this guy. This guy is an amazing developer. He stresses on good, clean code and pays heed to the details. I love developers who respect each and every aspect of a throughly thought out design and do their best to put it in code. He goes over and beyond and transforms ART into PIXELS - without a glitch, every time.", }, { name: 'June Cha', position: 'Software Engineer', photo: 'https://randomuser.me/api/portraits/women/44.jpg', text: 'This guy is an amazing frontend developer that delivered the task exactly how we need it, do your self a favor and hire him, you will not be disappointed by the work delivered. He will go the extra mile to make sure that you are happy with your project. I will surely work again with him!', }, { name: 'Iida Niskanen', position: 'Data Entry', photo: 'https://randomuser.me/api/portraits/women/68.jpg', text: "This guy is a hard worker. Communication was also very good with him and he was very responsive all the time, something not easy to find in many freelancers. We'll definitely repeat with him.", }, { name: 'Renee Sims', position: 'Receptionist', photo: 'https://randomuser.me/api/portraits/women/65.jpg', text: "This guy does everything he can to get the job done and done right. This is the second time I've hired him, and I'll hire him again in the future.", }, { name: 'Jonathan Nunfiez', position: 'Graphic Designer', photo: 'https://randomuser.me/api/portraits/men/43.jpg', text: "I had my concerns that due to a tight deadline this project can't be done. But this guy proved me wrong not only he delivered an outstanding work but he managed to deliver 1 day prior to the deadline. And when I asked for some revisions he made them in MINUTES. I'm looking forward to work with him again and I totally recommend him. Thanks again!", }, { name: 'Sasha Ho', position: 'Accountant', photo: 'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?h=350&auto=compress&cs=tinysrgb', text: 'This guy is a top notch designer and front end developer. He communicates well, works fast and produces quality work. We have been lucky to work with him!', }, { name: 'Veeti Seppanen', position: 'Director', photo: 'https://randomuser.me/api/portraits/men/97.jpg', text: 'This guy is a young and talented IT professional, proactive and responsible, with a strong work ethic. He is very strong in PSD2HTML conversions and HTML/CSS technology. He is a quick learner, eager to learn new technologies. He is focused and has the good dynamics to achieve due dates and outstanding results.', }, ] // データのインデックス let idx = 1 // アップデート処理 function updateTestimonial() { // JavaScriptだと{}+プロパティ名を指定すると // それぞれの変数に値を格納してくれる // ただし、同名に限る const { name, position, photo, text } = testimonials[idx] // データ格納 testimonial.innerHTML = text userImage.src = photo username.innerHTML = name role.innerHTML = position // インクリメント idx++ if (idx > testimonials.length - 1) { idx = 0 } } // 10秒間隔でアップデート処理を実行 setInterval(updateTestimonial, 10000) |
コメントを残す
コメントを投稿するにはログインしてください。