当前位置: 首页 > news >正文

前端 | 50天50个前端项目把握基础知识 - 持续更新中

50projects50days

项目介绍:50个简短前端小项目,每个文件夹包含了html,css,javascript三个文件.

- 必要知识:html,css,javascipt的基本定义

- 相互如何建立链接

17:Movie App 电影网站

查看现有的电影及评分,电影简介,应用API,可查找寻找电影。

index.html

<!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="style.css" />
    <title>Lina Notes </title>
  </head>
  <body>
    <header>
      <form id="form">
        <input type="text" id="search" class="search" placeholder="Search">
      </form>
    </header>

    <main id="main"></main>

    <script src="script.js"></script>
  </body>
</html>

 style.css

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;400&display=swap');

:root {
  --primary-color: #22254b;
  --secondary-color: #373b69;
}

* {
  box-sizing: border-box;
}

body {
  background-color: var(--primary-color);
  font-family: 'Poppins', sans-serif;
  margin: 0;
}

header {
  padding: 1rem;
  display: flex;
  justify-content: flex-end;
  background-color: var(--secondary-color);
}

.search {
  background-color: transparent;
  border: 2px solid var(--primary-color);
  border-radius: 50px;
  font-family: inherit;
  font-size: 1rem;
  padding: 0.5rem 1rem;
  color: #fff;
}

.search::placeholder {
  color: #7378c5;
}

.search:focus {
  outline: none;
  background-color: var(--primary-color);
}

main {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.movie {
  width: 300px;
  margin: 1rem;
  background-color: var(--secondary-color);
  box-shadow: 0 4px 5px rgba(0, 0, 0, 0.2);
  position: relative;
  overflow: hidden;
  border-radius: 3px;
}

.movie img {
  width: 100%;
}

.movie-info {
  color: #eee;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap:0.2rem;
  padding: 0.5rem 1rem 1rem;
  letter-spacing: 0.5px;
}

.movie-info h3 {
  margin-top: 0;
}

.movie-info span {
  background-color: var(--primary-color);
  padding: 0.25rem 0.5rem;
  border-radius: 3px;
  font-weight: bold;
}

.movie-info span.green {
  color: lightgreen;
}

.movie-info span.orange {
  color: orange;
}

.movie-info span.red {
  color: red;
}

.overview {
  background-color: #fff;
  padding: 2rem;
  position: absolute;
  left: 0;
  bottom: 0;
  right: 0;
  max-height: 100%;
  transform: translateY(101%);
  overflow-y: auto;
  transition: transform 0.3s ease-in;
}

.movie:hover .overview {
  transform: translateY(0);
}

 script.js

const API_URL = 'https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=3fd2be6f0c70a2a598f084ddfb75487c&page=1'
const IMG_PATH = 'https://image.tmdb.org/t/p/w1280'
const SEARCH_API = 'https://api.themoviedb.org/3/search/movie?api_key=3fd2be6f0c70a2a598f084ddfb75487c&query="'

const main = document.getElementById('main')
const form = document.getElementById('form')
const search = document.getElementById('search')

// Get initial movies
getMovies(API_URL)

async function getMovies(url) {
    const res = await fetch(url)
    const data = await res.json()

    showMovies(data.results)
}

function showMovies(movies) {
    main.innerHTML = ''

    movies.forEach((movie) => {
        const { title, poster_path, vote_average, overview } = movie

        const movieEl = document.createElement('div')
        movieEl.classList.add('movie')

        movieEl.innerHTML = `
            <img src="${IMG_PATH + poster_path}" alt="${title}">
            <div class="movie-info">
          <h3>${title}</h3>
          <span class="${getClassByRate(vote_average)}">${vote_average}</span>
            </div>
            <div class="overview">
          <h3>Overview</h3>
          ${overview}
        </div>
        `
        main.appendChild(movieEl)
    })
}

function getClassByRate(vote) {
    if(vote >= 8) {
        return 'green'
    } else if(vote >= 5) {
        return 'orange'
    } else {
        return 'red'
    }
}

form.addEventListener('submit', (e) => {
    e.preventDefault()

    const searchTerm = search.value

    if(searchTerm && searchTerm !== '') {
        getMovies(SEARCH_API + searchTerm)

        search.value = ''
    } else {
        window.location.reload()
    }
})

结果:

 总结:

基本样式 + CSS渐变 + CSS过渡特效 + 清除浮动。

JavaScript:接口请求 + 键盘事件的监听

33:Notes App 网页便签

index.html

<!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>Notes App</title>
  </head>
  <body>
    <button class="add" id="add">
      <i class="fas fa-plus"></i> Add note
    </button>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/1.2.2/marked.min.js"></script>
    <script src="script.js"></script>
  </body>
</html>

style.css

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;400&display=swap');

* {
  box-sizing: border-box;
  outline: none;
}

body {
  background-color: #7bdaf3;
  font-family: 'Poppins', sans-serif;
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  padding-top: 3rem;
}

.add {
  position: fixed;
  top: 1rem;
  right: 1rem;
  background-color: #9ec862;
  color: #fff;
  border: none;
  border-radius: 3px;
  padding: 0.5rem 1rem;
  cursor: pointer;
}

.add:active {
  transform: scale(0.98);
}

.note {
  background-color: #fff;
  box-shadow: 0 0 10px 4px rgba(0, 0, 0, 0.1);
  margin: 30px 20px;
  height: 400px;
  width: 400px;
  overflow-y: scroll;
}

.note .tools {
  background-color: #9ec862;
  display: flex;
  justify-content: flex-end;
  padding: 0.5rem;
}

.note .tools button {
  background-color: transparent;
  border: none;
  color: #fff;
  cursor: pointer;
  font-size: 1rem;
  margin-left: 0.5rem;
}

.note textarea {
  outline: none;
  font-family: inherit;
  font-size: 1.2rem;
  border: none;
  height: 400px;
  width: 100%;
  padding: 20px;
}

.main {
  padding: 20px;
}

.hidden {
  display: none;
}

script.js

const addBtn = document.getElementById('add')

const notes = JSON.parse(localStorage.getItem('notes'))

if(notes) {
    notes.forEach(note => addNewNote(note))
}

addBtn.addEventListener('click', () => addNewNote())

function addNewNote(text = '') {
    const note = document.createElement('div')
    note.classList.add('note')

    note.innerHTML = `
    <div class="tools">
        <button class="edit"><i class="fas fa-edit"></i></button>
        <button class="delete"><i class="fas fa-trash-alt"></i></button>
    </div>

    <div class="main ${text ? "" : "hidden"}"></div>
    <textarea class="${text ? "hidden" : ""}"></textarea>
    `

    const editBtn = note.querySelector('.edit')
    const deleteBtn = note.querySelector('.delete')
    const main = note.querySelector('.main')
    const textArea = note.querySelector('textarea')

    textArea.value = text
    main.innerHTML = marked(text)

    deleteBtn.addEventListener('click', () => {
        note.remove()

        updateLS()
    })

    editBtn.addEventListener('click', () => {
        main.classList.toggle('hidden')
        textArea.classList.toggle('hidden')
    })

    textArea.addEventListener('input', (e) => {
        const { value } = e.target

        main.innerHTML = marked(value)

        updateLS()
    })

    document.body.appendChild(note)
}

function updateLS() {
    const notesText = document.querySelectorAll('textarea')

    const notes = []

    notesText.forEach(note => notes.push(note.value))

    localStorage.setItem('notes', JSON.stringify(notes))
}

 结果:

 总结:

CSS:基本样式 + 卡片布局。

JavaScript:marked.js的使用 + localStorage API存储数据 + 鼠标光标位置的计算。

49:Todo List待办事项列表

index.html

<!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="style.css" />
    <title>Todo List</title>
  </head>
  <body>
    <h1>todos</h1>
    <form id="form">
      <input type="text" class="input" id="input" placeholder="Enter your todo" autocomplete="off">

      <ul class="todos" id="todos"></ul>
    </form>
    <small>Left click to toggle completed. <br> Right click to delete todo</small>

    <script src="script.js"></script>
  </body>
</html>

 script.js

const form = document.getElementById('form')
const input = document.getElementById('input')
const todosUL = document.getElementById('todos')

const todos = JSON.parse(localStorage.getItem('todos'))

if(todos) {
    todos.forEach(todo => addTodo(todo))
}

form.addEventListener('submit', (e) => {
    e.preventDefault()

    addTodo()
})

function addTodo(todo) {
    let todoText = input.value

    if(todo) {
        todoText = todo.text
    }

    if(todoText) {
        const todoEl = document.createElement('li')
        if(todo && todo.completed) {
            todoEl.classList.add('completed')
        }

        todoEl.innerText = todoText

        todoEl.addEventListener('click', () => {
            todoEl.classList.toggle('completed')
            updateLS()
        }) 

        todoEl.addEventListener('contextmenu', (e) => {
            e.preventDefault()

            todoEl.remove()
            updateLS()
        }) 

        todosUL.appendChild(todoEl)

        input.value = ''

        updateLS()
    }
}

function updateLS() {
    todosEl = document.querySelectorAll('li')

    const todos = []

    todosEl.forEach(todoEl => {
        todos.push({
            text: todoEl.innerText,
            completed: todoEl.classList.contains('completed')
        })
    })

    localStorage.setItem('todos', JSON.stringify(todos))
}

 style.css

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;400&display=swap');

* {
  box-sizing: border-box;
}

body {
  background-color: #f5f5f5;
  color: #444;
  font-family: 'Poppins', sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  margin: 0;
}

h1 {
  color: rgb(179, 131, 226);
  font-size: 10rem;
  text-align: center;
  opacity: 0.4;
}

form {
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
  max-width: 100%;
  width: 400px;
}

.input {
  border: none;
  color: #444;
  font-size: 2rem;
  padding: 1rem 2rem;
  display: block;
  width: 100%;
}

.input::placeholder {
  color: #d5d5d5;
}

.input:focus {
  outline-color: rgb(179, 131, 226);
}

.todos {
  background-color: #fff;
  padding: 0;
  margin: 0;
  list-style-type: none;
}

.todos li {
  border-top: 1px solid #e5e5e5;
  cursor: pointer;
  font-size: 1.5rem;
  padding: 1rem 2rem;
}

.todos li.completed {
  color: #b6b6b6;
  text-decoration: line-through;
}

small {
  color: #b5b5b5;
  margin-top: 3rem;
  text-align: center;
}

 运行结果:

 

相关文章:

  • 【智能优化算法-凌日搜索算法】基于凌日搜索算法求解单目标优化问题附matlab代码
  • C++11重写muduo网络库5——Thread,EventLoopThread,EventLoopThreadPool
  • NISP的渗透测试怎么操作的
  • 安装Nginx教程
  • Java_接口基本介绍
  • 16、Java——QuickHit游戏
  • SpringBoot--Controller获取HttpServletRequest
  • 牛客刷题笔记
  • 我把华为云的Ubuntu 18.04升级到了Ubuntu 22.04
  • Google Earth Engine-02(主界面介绍)
  • 5.java数据结构与算法 ---- 第七章 八大排序(冒泡;选择;插入/希尔;快排;归并;基数)
  • 新学期你立一个什么样的FLAG?
  • 【数据结构与算法】第九篇:哈希表原理
  • 自动驾驶江湖重新排位:毫末智行“重感知、轻地图”优势初现
  • [JS]变量
  • @angular/forms 源码解析之双向绑定
  • 【Amaple教程】5. 插件
  • 4. 路由到控制器 - Laravel从零开始教程
  • Apache的80端口被占用以及访问时报错403
  • docker容器内的网络抓包
  • Laravel 中的一个后期静态绑定
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • spring + angular 实现导出excel
  • Vim 折腾记
  • 简析gRPC client 连接管理
  • 将 Measurements 和 Units 应用到物理学
  • 小程序测试方案初探
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  •  一套莫尔斯电报听写、翻译系统
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • FaaS 的简单实践
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • ​如何防止网络攻击?
  • #pragma once与条件编译
  • (6)设计一个TimeMap
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (solr系列:一)使用tomcat部署solr服务
  • (zhuan) 一些RL的文献(及笔记)
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (八十八)VFL语言初步 - 实现布局
  • (转)大道至简,职场上做人做事做管理
  • (转)人的集合论——移山之道
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .NET Framework 4.6.2改进了WPF和安全性
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .Net 知识杂记
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [100天算法】-实现 strStr()(day 52)
  • [AIGC] Nacos:一个简单 yet powerful 的配置中心和服务注册中心
  • [Android] Amazon 的 android 音视频开发文档
  • [Android]Android P(9) WIFI学习笔记 - 扫描 (1)