Open2
お勉強3週目
- 前回作った自己紹介ページに、javascript で動くものを追加してみる (簡単なもので良い)
参孝
今日のサマリー
- お勉強1 ~ 2週目で写経した練習問題4をベースに、JavaScript: 操作の追加 を参考にした練習問題5の作成をClaudeに依頼
- 学習コストが少し高そう (内容が難しい...) だったので、基本的なインタラクティブな機能を3つのみに絞って追加した
- 練習問題5の写経、ブラウザでの確認を行なった
- AIがmdnページから参孝にした点を確認した
- 機能追加に伴い、
style
、body
、script
を追加の3つが必要なことを知った (大変...)
今日やったこと
1. 練習問題5を写経する
- 追加した3つのJavaScript機能
- テーマ切り替え(ダークモード)
- 名前入力と挨拶表示
- スキルバーのクリックアニメーション
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width", initial-scale="1.0">
<title>グエン福康のポートフォリオ - レスポンシブ版</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Hiragino Sans", "Meiryo", sans-serif;
line-height: 1.7;
color: #2c3e50;
background-color: #f7f8fa;
}
/* ダークモード用のスタイル */
body.dark-mode {
background-color: #1a1a2e;
color: #e8e8e8;
}
body.dark-mode .card{
background: #16213e;
color: #e8e8e8;
}
body.dark-mode header{
background: linear-gradient(135deg, #4a5fc1 0%, #6b4199 100%);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* テーマ切り替えボタン */
.theme-button {
position: fixed;
top: 20px;
right: 20px;
background: white;
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
font-size: 24px;
cursor: pointer;
box-shadow: 0.4px 15px rgba(0,0,0,0.1);
transition: transform 0.3s ease;
z-index: 100;
}
.theme-button:hover {
transform: scale(1.1);
}
header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 80px 20px;
text-align: center;
border-radius: 15px;
margin-bottom: 40px;
position: relative;
overflow: hidden;
}
header::before {
content: "";
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: rgba(255, 255, 255, 0.1);
transform: rotate(45deg);
}
header h1 {
font-size: 3em;
margin-bottom: 15px;
position: relative;
z-index: 1;
}
.tagline {
font-size: 1.3em;
opacity: 0.95;
position: relative;
z-index: 1;
}
/* 名前入力セクション */
.greeting-section {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0.4px 15px rgba(0,0,0,0.08);
margin-bottom: 30px;
text-align: center;
transition: background 0.3s ease;
}
body.dark-mode .greeting-section{
background: #16213e;
}
.greeting-section h2 {
color: #667eea;
margin-bottom: 20px;
}
#nameInput {
padding: 10px 15px;
border: 2px solid #e8e8e8;
border-radius: 8px;
font-size: 16px;
width: 250px;
margin-right: 10px;
}
#nameInput:focus {
outline: none;
border-color: #667eea;
}
#greetButton {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 10px 25px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: transform 0.3s ease;
}
#greeButton:hover {
transform: translateY(-2px);
}
#greetingMessage {
margin-top: 20px;
font-size: 1.2em;
color: #667eea;
font-weight: bold;
}
.content-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.main-content {
display: flex;
flex-direction: column;
gap: 30px;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 30px;
}
.card {
background: white;
padding: 35px;
border-radius: 15px;
box-shadow: 0.4px 15px rgba(0,0,0,0.08);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0.6px 20px rgba(0,0,0,0.12);
}
.card h2 {
color: #667eea;
margin-bottom: 20px;
font-size: 1.8em;
border-bottom: 2px solid #e8e8e8;
padding-bottom: 10px;
}
.progress-bar {
background-color: #e8e8e8;
height: 10px;
border-radius: 5px;
overflow: hidden;
margin: 10px 0;
}
.progress-bar:hover::after {
content: "クリックしてアニメーション";
position: absolute;
top: -25px;
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 3px 10px;
border-radius: 3px;
font-size: 12px;
white-space: nowrap;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
border-radius: 5px;
transition: width 1s ease;
}
.skill-item {
margin-bottom: 20px;
}
.skill-header {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
}
.timeline {
position: relative;
padding-left: 30px;
}
.timeline::before {
content: "";
position: absolute;
left: 8px;
top: 0;
bottom: 0;
width: 2px;
background: #667eea;
}
.timeline-item {
position: relative;
margin-bottom: 25px;
}
.timeline-item::before {
content: "";
position: absolute;
left: -26px;
top: 5px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #667eea;
border: 3px solid white;
box-shadow: 0.2px 5px rgba(0,0,0,0.2);
}
.timeline-date {
color: #667eea;
font-weight: bold;
margin-bottom: 5px;
}
footer {
background: #2c3e50;
color: white;
text-align: center;
padding: 30px;
border-radius: 15px;
}
.social-links {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
}
.social-link {
display: inline-block;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
line-height: 40px;
color: white;
text-decoration: none;
transition: background 0.3s sase;
}
.social-link:hover {
background: #667eea;
}
/* タブレット対応 */
@media (max-width: 768px) {
header h1 {
font-size: 2em;
}
.content-grid {
grid-template-columns: 2em;
}
.card {
padding: 25px;
}
}
/* スマートフォン対応 */
@media (max-width: 480px) {
header {
padding: 50px 15px;
}
header h1 {
font-size: 1em;
}
.tagline {
font-size: 1em;
}
.card {
padding: 20px;
}
.card h2 {
font-size: 1.4em;
}
}
</style>
</head>
<body>
<!-- 機能1: テーマ切り替えボタン -->
<button class="theme-button" onclick="toggleTheme()">🌙</button>
<div class="container">
<header>
<h1> グエン福康のポートフォリオ</h1>
<p class="tagline">一人前のエンジニアを目指して</p>
</header>
<!-- 機能2: 名前入力と挨拶表示 -->
<div class="greeting-section">
<h2>訪問者の方へ</h2>
<p>あなたのお名前を教えて下さい</p>
<div>
<input type="text" id="nameInput" placeholder="お名前を入力">
<button id="greetButton" onclick="showGreeting()">送信</button>
</div>
<div id="greetingMessage"></div>
</div>
<div class="content-grid">
<div class="main-content">
<div class="card">
<h2>自己紹介</h2>
<p>
こんにちは。グエンと申します。現在Web開発の世界に足を踏み入れたばかりの初心者エンジニアです。
毎日少しずつですが、コードを書く練習をしています。
</p>
<p>
将来的に自分でプロダクトを作るために、プログラミングを学習中です。
この自己紹介ページは、学習の一環として作成しています。
</p>
</div>
<div class="card">
<h2>スキルセット</h2>
<p style="margin-bottom: 20px; color: #999;"> プログレスバーをクリックするとアニメーションします</p>
<!-- 機能3: クリックでアニメーションするスキルバー -->
<div class="skill-item">
<div class="skill-header">
<span>HTML</span>
<span>70%</span>
</div>
<div class="progress-bar" onclick="animateSkillBar(this, 70)">
<div class="progress-fill" style="width: 70%;"></div>
</div>
</div>
<div class="skill-item">
<div class="skill-header">
<span>CSS</span>
<span>60%</span>
</div>
<div class="progress-bar" onclick="animateSkillBar(this, 60)">
<div class="progress-fill" style="width: 60%;"></div>
</div>
</div>
<div class="skill-item">
<div class="skill-header">
<span>JavaScript</span>
<span>30%</span>
</div>
<div class="progress-bar" onclick="animateSkillBar(this, 30)">
<div class="progress-fill" style="width: 30%;"></div>
</div>
</div>
<div class="skill-item">
<div class="skill-header">
<span>Git</span>
<span>40%</span>
</div>
<div class="progress-bar" onclick="animateSkillBar(this, 40)">
<div class="progress-fill" style="width: 40%;"></div>
</div>
</div>
</div>
</div>
<div class="sidebar">
<div class="card">
<h2>学習計画</h2>
<div class="timeline">
<div class="timeline-item">
<div class="timeline-date">現在</div>
<div>HTML/CSS基礎</div>
</div>
<div class="timeline-item">
<div class="timeline-date">1ヶ月後</div>
<div>JavaScript入門</div>
</div>
<div class="timeline-item">
<div class="timeline-date">3ヶ月後</div>
<div>React基礎</div>
</div>
<div class="timeline-item">
<div class="timeline-date">6ヶ月後</div>
<div>フルスタック開発</div>
</div>
</div>
</div>
<div class="card">
<h2>趣味</h2>
<p>📚読書</p>
<p>🎬映画鑑賞</p>
<p>☕️カフェ巡り</p>
<p>💻プログラミング学習</p>
</div>
</div>
</div>
<footer>
<h2>Contact</h2>
<p>お気軽にご連絡ください</p>
<div class="social-links">
<a href="#" class="social-link">📧</a>
<a href="#" class="social-link">💼</a>
<a href="#" class="social-link">🐦</a>
</div>
</footer>
</div>
<script>
// 機能1: テーマ(明暗)を切り替える関数
function toggleTheme() {
// body要素を取得
const body = document.body;
// ボタンを取得
const button = document.querySelector('.theme-button');
// dark-modeクラスがあるかチェック
if (body.classList.contains('dark-mode')) {
// ダークモードを解除
body.classList.remove('dark-mode');
// ボタンの絵文字を月に変更
button.textContent = '🌙';
} else {
// ダークモードを適用
body.classList.add('dark-mode');
// ボタンの絵文字を太陽に変更
button.textContent = '☀️';
}
}
// 機能2: 名前を入力して挨拶を表示する関数
function showGreeting() {
// 入力欄の要素を取得
const nameInput = document.getElementById('nameInput');
// 挨拶を表示する場所の要素を取得
const greetingMessage = document.getElementById('greetingMessage');
// 入力された名前を取得(前後の空白を削除)
const name = nameInput.value.trim();
// 名前が入力されているかチェック
if (name === '') {
// 名前が空の場合、メッセージを表示
greetingMessage.textContent = 'お名前を入力してください';
} else {
// 名前が入力されている場合、挨拶を表示
greetingMessage.textContent = 'こんにちは、' + name + 'さん!私のポートフォリオへようこそ!';
}
}
// 機能3: スキルバーをアニメーションする関数
function animateSkillBar(progressBar, targetPercent) {
// プログレスバー内の塗りつぶし部分を取得
const progressFill = progressBar.querySelector('.progress-fill');
// 一度0%にリセット
progressFill.style.width = '0%';
// 少し待ってから、目標の0%まで伸ばす
setTimeout(function() {
progressFill.style.width = targetPercent + '%';
}, 100);
}
</script>
</body>
</html>
2. 練習問題4との差分を確認
- style: 追加機能毎にstyleを追加
- body: 切り替えボタン/名前入力欄/アニメーションするスキルバーの追加
- script: 追加機能毎の関数を追加
3. ブラウザで見てみる
- file:///Users/nguyen/Desktop/my-portfolio/practice5/index.html
4. mdnページから参孝にした点を確認
- <script src="scripts/main.js"></script> を </body>タグの直前に配置
- querySelector() を使った要素の取得
- textContent プロパティでテキストを変更
機能1: テーマ切り替え(ダークモード)
mdnページには直接ない オリジナルの追加機能のようだが、以下の概念を応用している。
- 条件文の例:if...else 文
- classList の操作(DOM APIの応用)
機能2: 名前入力と挨拶表示
mdnの「パーソナライズされた挨拶メッセージの追加」セクションを簡略化しているそう。
// mdnの例
const myName = prompt("あなたの名前を入力してください。");
myHeading.textContent = `Mozilla はかっこいいよ、${myName} さん`;
// 練習問題5の例
const name = nameInput.value.trim();
greetingMessage.textContent = 'こんにちは、' + name + 'さん!';
機能3: スキルバーのクリックアニメーション
mdnの「画像の切り替えの追加」セクションの概念を応用しているそう。
// mdnの例:クリックで画像を切り替え
myImage.onclick = () => {
// src属性を変更
};
// 練習問題5の例:クリックでスキルバーをアニメーション
function animateSkillBar(progressBar, targetPercent) {
// widthスタイルを変更
}