基于HTML+CSS+JavaScript "小味鲜"餐厅网页设计
每博一文案
师父说“生活中的负能大多来于圈子里的抱怨“,有时候,你不想做别人情绪的垃圾桶。
却不得不接受他们的吐槽,你嫌弃身边人无休止的抱怨,但又不知不觉被他们影响
开始感慨日子艰辛,工作不如意,羡慕别人过得舒适,事事都称心。
其实,生活的重担下,没有谁活得特别轻松,懦弱的人,才会逢人就说自己不幸
坚强的人,都在咬紧牙关负重前行。
每个人都是自己的归宿,人和人之间,总是容易因为相似而互相吸引,兴趣相投才可能成为朋友
三观一致才可能成为伴侣,你是谁,就会遇见谁,你是谁,就会进入什么样的圈子。
与其责怪周围的不堪,不如调整自己的心态,不让自己也成为不堪中的一员。
与其羡慕别人的惬意,不如主动跳出舒适圈,积极努力地去成为更好的自己
要知道,你若乐观,挫折不会轻易打倒你,你若上进,生活不会总是亏待你。
想要看的远,得先站得高,苛责,不能让事情变好,抱怨,无法把日子变顺。
你只有内心丰富,才能摆脱生活的相似,你只有变得够强,才能进入更好的圈子
想拥有精致的外表,那就早睡早起坚持锻炼,想找到优秀的工作,那就提高实力增长经验
总有一天,你会站在新的高度,拥抱更美好的风景
愿你能享受最好的,也能承受最坏的,阳光下像个孩子,风雨里像个大人
知世故而不世故,会讲究也能将就。
—————— 一禅心灵庙语
文章目录
- 基于HTML+CSS+JavaScript "小味鲜"餐厅网页设计
- 每博一文案
- 1. 网站题目
- 2. 网站描述
- 3. 网站技术介绍
- 4. 网站效果图
- 5. 程序结构设计图
- 6. 具体相关代码实现
- 一. “小味鲜” 餐厅主界面实现
- 1. 页面元素和内容部分:HTML
- 2. 页面元素的外观和位置布局样式:CSS
- 3. 网页模型的定义与页面交互:JavaScript
- 二. 厨师力量界面的实现
- 1. 页面元素和内容部分:HTML
- 2. 页面元素的外观和位置布局样式:CSS
- 三. 快递包邮界面的实现
- 1. 页面元素和内容部分:HTML
- 2. 页面元素的外观和位置布局样式:CSS
- 四. 登录购餐界面的实现
- 1. 页面元素和内容部分:HTML
- 2. 页面元素的外观和位置布局样式:CSS
- 五. 注册界面的实现
- 1. 页面元素和内容部分:HTML
- 2. 页面元素的外观和位置布局样式:CSS
- 3. 网页模型的定义与页面交互:JavaScript
- 六. 订餐购餐界面的实现
- 1. 页面元素和内容部分:HTML
- 2. 页面元素的外观和位置布局样式:CSS
- 3. 网页模型的定义与页面交互:JavaScript
- 7. 总结
- 8. 最后:
1. 网站题目
简单的描述:一个餐厅的页面的主要功能,登录页面,注册页面,人物介绍,订餐购物车
2. 网站描述
生活当需麻辣鲜香,方能体验人生百味。美食,永远都是人们最为关注的一个话题,因为没有任何人能够阻挡美食的诱惑,尤其是现代社会下,我们对于美食的追求也越来越明显,以前可能还满足于温饱,但现在更多的是追求食物的本身,怎么吃到更多的美食是现代人经常所想的事情,因此旅行也帮助我们吃到其他不同的美食,甚至有些人环球旅行,也是为了美食而展开。提起美食,那么就不得不说到餐厅,
3. 网站技术介绍
网页作品代码简单,可使用任意 HTML 编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad++
等任意html编辑软件进行运行及修改编辑等操作)。
其中:
(1)html 文件包含:其中 index.html
是首页、其他html
为二级页面;
(2)css 文件包含:css 全部页面样式,文字滚动, 图片放大等;
(3)js 文件包含:点击事件,简单的登录注册操作,等等(个别网页中运用到js代码)
4. 网站效果图
该“ 小味鲜 ”餐单网站主要实现了 六 界面分别为:餐厅主页面,餐厅登录购餐,餐厅注册,餐厅厨师力量介绍,餐厅订餐购物,快递包邮 。
小味鲜餐厅主界面
餐厅登录购餐页面
餐厅注册页面
餐厅厨师力量介绍
餐厅订餐购物主界面
快递包邮
5. 程序结构设计图
对应一个项目的创建,我们需要先拟定好,架构图,分好主干
,架构图拟定好了,主干清晰了,这样无论你怎么写,都不会太偏离主干 ,写的代码才不会乱,犯浑。干起来也不会太累。
我们需要分层处理,不同层,干不同的事情,分好包,再分好对应的类来。各司其职,就像 TCP七层协议一样,那一层该干什么就做什么事情,其他层的业务你不用管。如下是 架构图
-
“小味鲜” 主界面:进入网页中看到的第一个页面(LOGO、公司名称、导航、banner、新闻、相关信息、底部信息、创建一个主界面网站,附加一些主要的导航,索引,链接(如:厨师力量,登录购餐,快递包邮)
-
登录购餐,登录成功即可跳转到点餐服务,没有账号可以注册一下。
架构图 拟定好了,我们先根据架构图, 进行一个包(文件夹)类的划分分类
- img: 存放有关界面引用的图片素材
- ccs: 存放有关界面的样式布局
- js : 存放有关界面的样式事件的样式处理。
6. 具体相关代码实现
如下是各个相关界面的代码的具体实现:
一. “小味鲜” 餐厅主界面实现
1. 页面元素和内容部分:HTML
同样对于一个页面的布局,我们需要从整体再到一个一个的细节的把握 ,通过header,article,footer
进行一个整体的划分,页眉,页中,页尾。再通过多个 div
块对不同细节上页面显示划分的处理。
完整实现代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小味鲜</title>
<link rel="stylesheet" href="./style.css" /> <!-- 导入css样式-->
<!-- <link rel="stylesheet" href="..//index.html"> -->
</head>
<body >
<div id="content">
<header>
<!-- header:文档的页眉显示-->
<nav>
<!--nav:标签定义导航链接的部分-->
<ul>
<!--ul:定义无序列表-->
<li><a href="#">首页</a></li> <!-- li:列数 <a> 标签的 href 属性用于指定超链接目标的 URL-->
<li><a href="#">美食系列</a></li>
<li><a href="#">公司简介</a></li>
<li><a href="../人物介绍/index.html">厨师力量</a></li>
<li><a href="#">关于我们</a></li>
</ul>
</nav>
<hr /> <!-- hr加一个横线-->
<div id="head_text">
<p>"小味鲜"</p>
<input type="button" value="登录购餐" οnclick="window.open('../登录/index.html','_self')" />
<!-- 注意单双引号的交替使用,_self跳转的是当前页面的窗口-->
</div>
<!------------------------------------------------------------->
<div id="head_icon">
<div class="icon"><img src="./img/con1.jpg" />
<p>用户支持</p>
</div>
<div class="icon"><img src="./img/con2.jpg" />
<p>品质保证</p>
</div>
<div class="icon"><img src="./img/con3.jpg" />
<p>精致搭配</p>
</div>
<div class="icon">
<img src="./img/con4.jpg" />
<p οnclick="window.open('../枫叶动画/index.html')">快递包邮</p>
</div>
</div>
</header>
<!------------------------------------------------------------->
<article>
<!--<article> 标签定义外部的内容。
外部内容可以是来自一个外部的新闻提供者的一篇新的文章,或者来自 blog 的文本,或者是来自论坛的文本。亦或是来自其他外部源内容。
-->
<div id="content1">
<div id="content1_img">
<div class="content">
<p>ENJOY YOUR LIFE</p>
<p>享受生活,愉悦自我</p>
</div>
<div class="img"></div>
</div>
<div id="content1_text">
<p>ENJOY LIFE AND BE HAPPY</p><br>
<h4>品牌故事</h4>
<p>Brand story</p>
<hr />
<p>我们是森林中的动物,寻找栖身之所,寻找同伴,寻找爱</p><br />
<input type="button" value="了解详情" />
<p>新鲜食材 / 厨艺大师 / 烹饪技巧 / 故事</p>
</div>
</div>
<!------------------------------------------------------------->
<div id="content2">
<div class="content">
<h5>Canteen show</h5>
<p>餐厅展示</p>
</div>
<div class="img"></div>
</div>
<!------------------------------------------------------------->
<div id="content3">
<div id="comtent3_imgs">
<div id="content3_img"></div>
<div id="content3_img"></div>
<div id="content3_img"></div>
<div id="content3_img"></div>
<div id="content3_img"></div>
</div>
</div>
<!------------------------------------------------------------->
<div id="content4">
<div class="content">
<h5><b>Superior private room</b></h5>
<p>高级包间</p>
</div>
<div class="img">
<img src="./img/Canteen2.jpeg" />
</div>
</div>
<!------------------------------------------------------------->
<div id="content5">
<div id="content">
<h5>Special Dishes</h5>
<p>特色菜品</p>
</div>
<div class="img">
<div class="content5_img"></div>
<div class="content5_img"></div>
<div class="content5_img"></div>
</div>
<div class="content">
<div class="content5_content">
<h5><b>手提式花盆</b></h5>
<p>手提花盒,不仅仅是汇聚了智慧与审美,同时也逐渐发展为了一个新的潮流,独特的造型简约的风格,让广大的爱花人群,多了一个极致的选择。</p>
<input type="button" value="了解更多" />
</div>
<div class="content5_content">
<h5><b>盒装花盆</b></h5>
<p>圆形,总是那样给予人无限魔力,授予艺术最大的想象空间,当鲜花进入一个圆形,那般自然和美妙,可以说,圆形花盒,总能给人带来浑然天成的亲切感。</p>
<input type="button" value="了解更多" />
</div>
<div class="content5_content">
<h5><b>方形花盆</b></h5>
<p>花盒中最为古典传统的一种,可以充分利用几何空间,在一个平面上最大限度地发挥自己的创造思维,给人带来了古典与现代并存的庄重感与仪式感。</p>
<input type="button" value="了解更多" />
</div>
</div>
</div>
</article>
<!------------------------------------------------------------->
<footer>
<!--<footer> 标签定义 section 或 document 的页脚。-->
<div id="footer1">
<p> 190-3829-8009 </p>
<p>27912213258@qq.com</p>
</div>
<div id="footer2">
<div id="footer_a">
<a herf="#">首页</a>
<a herf="#">品牌故事</a>
<a href="#">人才招聘</a>
<a href="#">新闻资讯</a>
</div>
<div class="order">
<span class="line"></span>
<span class="txt">工作时间: 周一至周五9:00-18:00</span>
<span class="line"></span>
</div>
</div>
<div id="footer3">
<div class="icon"><img src="./img/con1.jpg" /></div>
<div class="icon"><img src="./img/con2.jpg" /></div>
<div class="icon"><img src="./img/con3.jpg" /></div>
<div class="icon"><img src="./img/con4.jpg" /></div>
</div>
</footer>
</div>
</body>
</html>
2. 页面元素的外观和位置布局样式:CSS
注意在编写 css
样式之前我们需要做一个 css初始化
css 初始化的含义: css 初始化是指重新设置浏览器的样式。不同的浏览器默认的样式可能存在差异,所以开发时的第一件事情就是如何把它们统一,即 css初始化
css初始化的目的: 主要是为了考虑都浏览器的兼容问题,不同的浏览器对有些标签的默认值是不同的,如果没有对 CSS 初始化,往往会出现浏览器之间的页面差异。每次新开发网站或网页的时候,一般都会通过初始化 CSS样式的属性,为我们将用到的CSS或 HTML标签更加方便准确,使得我们开发网页内容更加方便简洁,同时减少 CSS 代码量,节约网页下载时间。如果不初始化,整个页面做完了可能会很糟糕,重复的 CSS 样式会很多。当然,初始化样式会对 SEO 有一定的影响,但力求影响最小的情况下,还是要初始化 CSS 的
初始化 CSS的方法 最简单的初始化就是
/* 初始化界面 */
* {
margin: 0;
padding: 0;
/* 标准盒子 */
box-sizing: content-box;
}
但是这样一个 *
星号通用符在,编写代码的时候是快了,但如果网站很大,CSS样式表文件很大的话,这么做就会把所有的标签都初始化一遍了。这样就会大大的加强了网站运行的负载,会使网站加载过慢。
大家可以从百度上找一些 大厂 京东,阿里,腾讯
这类的 CSS 初始化。
如下是淘宝的CSS初始化代码如下:
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; }
body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; }
h1, h2, h3, h4, h5, h6{ font-size:100%; }
address, cite, dfn, em, var { font-style:normal; }
code, kbd, pre, samp { font-family:couriernew, courier, monospace; }
small{ font-size:12px; }
ul, ol { list-style:none; }
a { text-decoration:none; }
a:hover { text-decoration:underline; }
sup { vertical-align:text-top; }
sub{ vertical-align:text-bottom; }
legend { color:#000; }
fieldset, img { border:0; }
button, input, select, textarea { font-size:100%; }
table { border-collapse:collapse; border-spacing:0; }
如下是京东的CSS初始化代码
/*所有元素的内外边距清除*/
* {
margin: 0;
padding: 0
}
/*em/i是斜体字 让他变正*/
em,
i {
font-style: normal
}
/*去掉列表的圆形点装饰*/
li {
list-style: none
}
/* 图片上有连接可能会导致图片出现边框让边框变无,vertical-align去掉图片底部的基线空白缝隙还能让旁边的文字垂直居中*/
img {
border: 0;
vertical-align: middle
}
/*按钮上鼠标变手*/
button {
cursor: pointer
}
/*超链接去掉下划线并变色*/
a {
color: #666;
text-decoration: none
}
/*:hover让鼠标经过连接变红*/
a:hover {
color: #c81623
}
/*这些都是字体类型\5B8B\4F53是宋体的Unicode编码显示不然中文乱码*/
button,
input {
font-family: Microsoft YaHei, Heiti SC, tahoma, arial, Hiragino Sans GB, "\5B8B\4F53", sans-serif
}
body {
/*css3的文字放大抗锯齿*/
-webkit-font-smoothing: antialiased;
background-color: #fff;
font: 12px/1.5 Microsoft YaHei, Heiti SC, tahoma, arial, Hiragino Sans GB, "\5B8B\4F53", sans-serif;
color: #666
/*字体的大小是12px行高1.5*/
}
.hide,
.none {
display: none
}
.clearfix:after {
visibility: hidden;
clear: both;
display: block;
content: ".";
height: 0
/*清除浮动的伪类选择器就是给添加了这个.clearfix的元素后面添加一个元素 visibility让他不显示 它必须有content 他默认是行内元素
因为我们清除浮动的基本原理就是给他内部最后一个元素添加一个div(块级元素)让他占有最下面使盒子撑开
然后给他清除浮动
*/
}
.clearfix {
*zoom: 1
/*Ie6 ie7专属清除浮动*/
}
完整代码如下:
/* 初始化界面 */
* {
margin: 0;
padding: 0;
/* 标准盒子 */
box-sizing: content-box;
}
/* 界面整体布局样式 */
#content {
/*div:contentid选择器*/
height: 30%;
width: 60%;
margin-left: 300px;
/* margin-left:左对齐 */
}
/* 页眉部分的大小布局 */
header {
background-image: url(./img/foot1.jpeg);
height: 500px;
width: 100%;
/* 多余大小的背景填充*/
/* background-image 属性会在元素的背景中设置一个图像,url图片的路径
background-repeat 属性设置是否及如何重复背景图像,默认地,背景图像在水平和垂直方向上重复,no-repeat 背景图像将仅显示一次。
background-size使用contain自适应填满div块,cover长宽都拉伸填满
background-color : transparent | color 属性设置元素的背景颜色,transparent :背景色透明*/
}
/* 页眉中链接的布局 */
nav,
ul {
height: 55px;
/* 弹性布局,flex 横向布局 */
display: flex;
/* 上下垂直居中 */
align-items: center;
/* 左右水平居中 */
justify-content: center;
}
/*页眉列表的设置*/
nav ul li {
/* 去掉无序的标志点 */
list-style: none;
/* 字体大小 */
font-size: 20px;
/* 向右打内补丁 */
/* padding-right: 38px; */
margin-right: 38px;
/* 文本居中 */
text-align: center;
}
/* 页眉链接的处理 */
nav a {
/* 去除链接的下划线 */
text-decoration: none;
color: white;
}
/* 导航栏中,鼠标点击显示的样式 */
nav a:hover {
color: rgb(65, 176, 224);
/* :hover 伪类在鼠标移到元素上时向此元素添加特殊的样式。 */
}
/* 满汉楼文字样式 */
#head_text {
/* 相对定位 */
position: relative;
/* 向左外补丁,控制位置 */
margin-left: 150px;
margin-top: 80px;
font-family: "华文新魏";
/* 字体大小 */
font-size: 70px;
color: white;
}
/* 登录购餐样式处理 */
#head_text input {
/*input 按钮的设置*/
height: 100px;
width: 135px;
/* 边框的设置,边框的粗细,边框的虚实线,边框的颜色,这里设置没有边框显示 */
border: 0px;
color: rgb(89, 95, 111);
/* 文字大小 */
font-size: 30px;
/* 文字的粗细 */
font-weight: 700;
/* 边框,按钮,背景透明 */
background-color: transparent;
/* background-color : transparent | color 属性设置元素的背景颜色,transparent :背景色透明
或者: background-color: rgba(0, 0, 0, 0.1);按钮定义背景颜色可使用rgba() */
}
/* 设置点击登录购餐时,显示的背景颜色,以及小手 */
#head_text input:hover {
color: rgb(227, 31, 27);
/* 鼠标小手 */
cursor: pointer;
}
/* 文字用户支持,品质保证整体布局的样式处理 */
#head_icon {
height: 140px;
/* 相对定位 */
position: relative;
/* 顶部外补丁,控制间距 */
margin-top: 70px;
/* 弹性布局,flex div盒子横向排列不会独占行 */
display: flex;
/* div盒子水平居中 */
justify-content: center;
}
.icon {
height: 100px;
width: 100px;
/* 右外打补丁,控制间距 */
margin-right: 40px;
}
/* 头部的图片样式处理 */
.icon img {
/* 外补丁:四个参数上右下左 */
margin: 10px 20px 0px 30px;
/* 图片的透明度 */
filter: opacity(0.6);
/* 边框的锐度,50%是圆形 */
border-radius: 50%;
/*
filter 属性允许您向文本和图像添加更多的样式效果,若需要使用 filter 属性,请始终指定元素的宽度。
元素外边距内就是元素的的边框 (border)。元素的边框就是围绕元素内容和内边据的一条或多条线。
radius 定义圆弧的圆的半径。
filter定义颜色透明度opacity(0.6)值越小越透明,border-radius属性当图片长宽一样时设定50%为圆形
*/
}
/* 文字用户支持,品质保证文字样式处理 */
.icon p {
margin-left: 20px;
margin-top: 0;
color: white;
font-size: 20px;
}
/* 鼠标点击用户支持,品质保证小手 */
.icon p:hover {
color: red;
/* 鼠标小手 */
cursor: pointer;
}
aritcle {
height: 1200px;
width: 450px;
margin-left: 130px;
/*
<article> 标签定义外部的内容。
外部内容可以是来自一个外部的新闻提供者的一篇新的文章,或者来自 blog 的文本,或者是来自论坛的文本。亦或是来自其他外部源内容。
*/
}
#content1 {
height: 250px;
/* 顶部打外补丁控制边距 */
margin-top: 20px;
}
#content1_img {
height: 100%;
width: 220px;
/* 左外补丁 */
margin-left: 150px;
/* 向左浮动 */
float: left;
}
#content1_img .content {
height: 25%;
/* 字体大小 */
font-size: 10px;
}
#content1_img .img {
height: 80%;
/* 导入背景图片 */
background: url(./img/log.jpg) no-repeat;
/* 设置背景图片的大小 */
background-size: contain;
/*
background-size: cover;指定背景图像的大小
ackground: url(../images/flower1.jpeg) no-repeat:属性,用于在样式表的同一位置设置大多数背景属性,这里是导入图片
auto: 背景图像的真实大小。 cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器。 contain: 将背景图像等比缩放到宽度或高度与容器的宽度或高度相等,背景图像始终被包含在容器内。
*/
}
#content1_text {
height: 100%;
width: 220px;
/* 左外补丁 */
margin-left: 10px;
/* 左浮动 */
float: left;
}
#content1_text p {
/* 文字大小 */
font-size: 4px;
/* 文字样式 italic斜体*/
font-style: italic;
color: gray;
margin-top: 10px;
}
#content1_text input {
background-color: #E78E87;
color: white;
/* 字体大小 */
font-size: 4px;
height: 20px;
width: 70px;
/* 隐藏边框 none */
border: none;
/* 边框锐度,50%为圆 */
border-radius: 10px;
}
/* 鼠标点击用户支持,品质保证小手 */
#content1_text input:hover {
color: red;
/* 鼠标小手 */
cursor: pointer;
}
#content2 {
margin-top: 30px;
height: 300px;
/* 文本居中 */
text-align: center;
font-size: 30px;
}
#content2 .img {
height: 80%;
margin-top: 20px;
background: url(./img/Canteen.webp) no-repeat;
/* 图片居中显示占页面的50% */
background-position: 50%;
}
#content3 {
height: 150px;
/* 上外补丁 */
margin-top: 90px;
}
#comtent3_imgs {
/* 上内补丁 */
position: relative;
padding-top: 10px;
height: 100%;
width: 100%;
/* 弹性布局 flex div盒子横向排列,不会独占行 */
display: flex;
flex-direction: row;
/* 水平居中 */
justify-content: center;
align-items: center;
/* flex-direction: row指定如何将伸缩项放置在伸缩容器中。*/
}
#content3_img {
width: 20%;
height: 100%;
margin-right: 50px;
}
/*使用结构选择器对同一层div块插入背景*/
#content3_img:nth-child(1) {
background: url(./img/reception.webp) no-repeat;
background-size: cover;
}
#content3_img:nth-child(2) {
background: url(./img/reception2.jpg) no-repeat;
background-size: cover;
}
#content3_img:nth-child(3) {
background: url(./img/reception3.jpg) no-repeat;
background-size: cover;
}
#content3_img:nth-child(4) {
background: url(./img/reception4.jpg) no-repeat;
background-size: cover;
}
#content3_img:nth-child(5) {
background: url(./img/reception5.jpg) no-repeat;
background-size: cover;
}
#content4 {
margin-top: 30px;
height: 300px;
/* 文本居中 */
text-align: center;
/* 文字大小 */
font-size: 30px;
}
#content4 .img {
margin-top: 30px;
width: 100%;
}
#content4 img {
width: 70%;
/* 边框设置 内补丁*/
border-radius: 120px 0 0 120px;
}
#content5 {
height: 250px;
margin-top: 100px;
}
#content5 #content {
/* display: flex;
flex-direction: column; */
/* 文字居中 */
text-align: center;
/* 文字大小 */
font-size: 30px;
/* 相对定位 */
position: relative;
/* 外补丁,文字显示居中 */
margin-bottom: 50px;
margin-left: 250px;
}
#content5 .img {
height: 100%;
/* 弹性布局,flex 横向布局,div盒子不会独占行 */
display: flex;
/* flex-direction: row; */
/* 水平两端对齐 */
justify-content: space-between;
}
.content5_img {
height: 100%;
width: 30%;
}
/*使用结构选择器设置样式*/
.content5_img:nth-child(1) {
/* 背景图片 */
background: url(./img/Canteen.webp) no-repeat;
/* 指定图片大小 */
background-size: cover;
/* 边框锐度,50%是圆 */
border-radius: 0 50px;
/*
auto: 背景图像的真实大小。
cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器。
contain: 将背景图像等比缩放到宽度或高度与容器的宽度或高度相等,背景图像始终被包含在容器内 */
}
.content5_img:nth-child(2) {
background: url(./img/Canteen2.jpeg) no-repeat;
background-size: cover;
border-radius: 0 50px;
}
.content5_img:nth-child(3) {
background: url(./img/reception.webp) no-repeat;
background-size: cover;
border-radius: 0 50px;
}
#content5 .content {
height: 50%;
/* 弹性布局,flex 横向布局,div盒子不会独占行 */
display: flex;
/* 水平两端对齐 */
justify-content: space-between;
}
.content5_content {
height: 200px;
width: 30%;
}
.content5_content h5 {
margin-top: 20px;
/* 外打补丁 */
/* margin-bottom: 0; */
/* 文字大小 */
font-size: 20px;
}
.content5_content p {
margin-top: 10px;
color: gray;
/* 文字大小 */
font-size: 15px;
}
.content5_content input {
height: 25px;
width: 90px;
font-size: 6px;
color: white;
/* 边框不显示 */
border: none;
background-color: #E78E87;
margin-left: 50px;
margin-top: 20px;
/* 边框的锐度,50%是圆 */
border-radius: 15px;
}
/* 了解更多,鼠标经过小手样式 */
.content5_content input:hover{
/* 鼠标小手样式 */
cursor: pointer;
color: red;
}
footer {
height: 200px;
background-color: #E78E87;
color: white;
/* 字体大小 */
font-size: 6px;
/* 相对定位 */
position: relative;
margin-top: 350px;
}
#footer1 {
height: 40%;
/* margin-top: 200px; */
/* 文本居中 */
/* text-align: center; */
}
/* 控制文字居中 */
#footer1 p {
margin-left: 200px;
margin-top: 20px;
}
/*使用结构选择器设置样式*/
#footer1 p:nth-child(1) {
/* 左浮动 */
float: left;
}
#footer1 p:nth-child(2) {
float: left;
}
#footer2 {
width: 80%;
margin-left: 10%;
}
/* 两个分界线 */
.order {
height: 0px;
line-height: 30px;
text-align: center;
}
.order .line {
/* 弹性布局,inline-block div盒子以块的形式显示,不会独占行 */
display: inline-block;
/* 横线的宽度 */
width: 200px;
/* 边框设置,边框粗细,边框的虚实线样式,边框颜色 */
border-top: 1px solid white;
/*display: inline-block; 对齐方式*/
}
.order .txt {
color: white;
/* 文字垂直排列 */
vertical-align: -3px;
/*vertical-align: 影响行内框内的行内级元素生成的行内框的垂直位置。*/
}
#footer_a {
/* 外打补丁 */
margin-top: 10px;
margin-left: 150px;
}
#footer_a a {
/* 去除链接的下划线 */
text-decoration: none;
color: white;
/* 外补丁,两个参数:上下,左右 */
margin: 0 20px;
}
/* 页尾导航,鼠标小手样式 */
#footer_a a:hover{
/* 小手样式 */
cursor: pointer;
color: red;
}
#footer3 {
height: 30%;
/* 左打内补丁 */
padding-left: 250px;
display: flex;
margin-top: 10px;
}
#footer3 .icon {
width: 10%;
height: 100%;
margin-left: 10px;
margin-top: 35px;
}
#footer3 img {
height: 25px;
width: 25px;
/* 图片背景的透明度 */
filter: opacity(0.8);
margin: 0;
/*
filter 属性允许您向文本和图像添加更多的样式效果,若需要使用 filter 属性,请始终指定元素的宽度。
filter定义颜色透明度opacity(0.6)值越小越透明,border-radius属性当图片长宽一样时设定50%为圆形
*/
}
3. 网页模型的定义与页面交互:JavaScript
因为是主界面这里涉及的的 javascript 代码比较的少,基本上就是一些跳转时的一些细节的处理。
具体实现了如下功能:
- 点击厨师力量跳转
- 点击登录购餐
需要注意的是,这里我们的 登录购餐 跳转的时候使用的是 onclick
的一个点击事件中 window内置的对象open
需要注意的点就是,这里我们在 url 的后面,附加上了 _self
这个参数,表示点击跳转的时候,不会创建新窗口而是刷新当前窗口,以及注意在事件调用的函数时,传参数时,注意 单双引号
交替使用,不然无法调用函数的。
<div id="head_text">
<p>"小味鲜"</p>
<input type="button" value="登录购餐" onclick="window.open('../登录/index.html','_self')" />
<!-- 注意单双引号的交替使用,_self跳转的是当前页面的窗口-->
</div>
- 登录注册页面
- 点击快递包邮
二. 厨师力量界面的实现
1. 页面元素和内容部分:HTML
同样我们需要 先从整体到局部 ,一一实现,具体结构如下
- 整体上三张人物介绍图,我们可以定义 三个大的
div
盒子,每个盒子一张图片。 - 我们只需要处理一张图片的样式,其他两张图片的样式都是一样的,所以在定义类上类名一致。
- 首先是定义插入的图片的
div
盒子,并通过img
导入图片 - 每个人物下面有一些文字介绍,根据大小我们分别定义为
h1,h2,p
- 最后一个就是
了解更多
的索引了,这里我们使用的是a
链接,大家也可以使用button
按钮
具体完整代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>人物介绍卡</title>
<!-- 导入css样式 -->
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!--先从整体结构,开始构建-->
<!--三个人物介绍,三个整体的div盒子-->
<div class="card">
<div class="photo">
<img src="./personage1.jpg" alt="">
</div>
<h1>主厨</h1>
<h2>萧瑟</h2>
<p>手提花盒,不仅仅是汇聚了智慧与审美,同时也逐渐发展为了一个新的潮流,独特的造型简约的风格,让广大的爱花人群,多了一个极致的选择。</p>
<a href="#">了解更多</a>
</div>
<div class="card">
<div class="photo">
<img src="./personage2.webp" alt="">
</div>
<h1>主厨</h1>
<h2>萧炎</h2>
<p>圆形,总是那样给予人无限魔力,授予艺术最大的想象空间,当鲜花进入一个圆形,那般自然和美妙,可以说,圆形花盒,总能给人带来浑然天成的亲切感。</p>
<a href="#">了解更多</a>
</div>
<div class="card">
<div class="photo">
<img src="./personage3.jpg" alt="">
</div>
<h1>主厨</h1>
<h2>韩立</h2>
<p>花盒中最为古典传统的一种,可以充分利用几何空间,在一个平面上最大限度地发挥自己的创造思维,给人带来了古典与现代并存的庄重感与仪式感。</p>
<a href="#">了解更多</a>
</div>
</body>
</html>
2. 页面元素的外观和位置布局样式:CSS
- 同样第一步初始化CSS样式,兼容浏览器
CSS的样式布局同样也是从整体到局部的,先处理整体布局(比如整体的文字,以及背景色),再一点一点的处理,小的div的布局样式
/* 初始化界面 */
* {
/* 上下左右外不补丁为0 */
margin: 0;
/* 上下左右内不补丁为0 */
padding: 0;
}
设置 body 标签的样式,让三张人物图片居中。需要注意的点就是,这里我们的背景颜色使用的是 linear-gradient(200deg, #517fa4, #243949);
一个渐变色的颜色,我们从百度上找到的。
/* 整体布局的样式 */
body {
/* vh 是视图单位 */
height: 100vh;
/* 弹性布局,默认横向排列 */
display: flex;
/* 弹性布局水平居中 */
justify-content: center;
/* 弹性布局,上下垂直居中 */
align-items: center;
/* 背景颜色设置为渐变色 */
background: linear-gradient(200deg, #517fa4, #243949);
/* deg是CSS中的一个角度单位,表示度(Degress),一个圆共360度。在CSS中角度单位有:度(deg)、梯度(grad)、弧度(rad) */
}
设置所有插入的图片的一个布局,因为我们三张图片的类名都是一样的,这里就体现同类名的好处,方便统一管理
- 需要注意的点就是:
border-radius: 20px;
可以处理边框的锐度,让边框不是一个正方的,而是圆润的,50%是一个圆overflow: hidden;
处理图片填充时的大小超过了,我们所定义的大小高度,从而覆盖了,我们上面的边框锐度处理,而这个属性可以,隐藏我们图片溢出的部分,去除超出边界的部分。box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
设置边框的凹凸的阴影效果,具体参数含义如下- none: 无阴影
- 第 1 个长度值定义元素的阴影水平偏移值。正值,阴影出现在元素右侧;负值,则阴影出现在元素左侧
- 第 2 个长度值定义元素的阴影垂直偏移值。正值,阴影出现在元素底部;负值,则阴影出现在元素顶部
- 第 3 个长度值定义元素的阴影模糊值半径(如果提供了)。该值越大阴影边缘越模糊,若该值为
0
,阴影边缘不出现模糊。不允许负值 - 第 4 个长度值定义元素的阴影外延值(如果提供了)。正值,阴影将向四面扩展;负值,则阴影向里收缩
- inset: 定义元素阴影的颜色。如果该值未定义,阴影颜色将默认取当前最近的文本颜色
flex-shrink
设置或检索弹性盒的收缩比率,根据弹性盒子元素所设置的收缩因子作为比率来收缩空间
/* 所有卡片的布局 */
.card {
/* 相对定位 */
position: relative;
width: 300px;
height: 500px;
/* 上下左右外补丁 */
margin: 20px;
background-color: #758a99;
/* 去除边框的锐度 */
border-radius: 20px;
/* 溢出隐藏,去除超出边界的部分 */
overflow: hidden;
/* 弹性布局,flex默认横向排列,none不显示,block以块的方式显示独占行,inline-block:以块的形式显示,并不会独占行 */
display: flex;
/* 设置元素信息纵向排列column:主轴与块轴方向作为默认的书写模式。即纵向从上往下排列(顶对齐) */
flex-direction: column;
/* 上下垂直居中 */
align-items: center;
color: #fff;
/* 设置阴影凹陷 */
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
/* 根据弹性盒子元素所设置的收缩因子作为比率来收缩空间。防止被其他盒子压缩导致图片缩小
注意:如果元素不是弹性盒对象的元素,则 flex-shrink 属性不起作用。 */
flex-shrink: 0;
}
对于 img 图片的定位我们需要,进行一个填充的样式处理使用 object-fit: cover;
上传图片的时候遇到了图片变形的问题,最后通过object-fit: cover完美解决了。这个CSS属性可以达到最佳最完美的居中自动剪裁图片的功能
/* 设置所有图片的样式 */
.card .photo img {
width: 100%;
height: 100%;
/* 保持图片原有的尺寸比例填充,防止失帧 */
object-fit: cover;
}
- 设置图片最开始时显示的是大图,
这里我们使用了 transition: 0.5s;
检索或设置对象过渡的持续时间 ,也就是设置加载图片时,有一个过渡的动画时间的效果。
/* 设置最开始显示的大图 */
.card .photo {
/* 绝对定位 */
position: absolute;
top: 0;
width: 100%;
height: 100%;
/* 去除边框的锐度 */
border-radius: 0%;
/* 隐藏溢出的边角,隐藏超过边角的角 */
overflow: hidden;
/* 动画过渡 设置加载延迟时间*/
transition: 0.5s;
}
- 通过伪类选择符
:hover
设置元素在其鼠标悬停时的样式。设置鼠标移动到图片所在div 类名为card
块时,显示变成小图片。一个圆的小图片( border-radius: 50%;设置边框锐度,50%为圆),并显示在最头顶(使用定位)
/* 设置鼠标移入到图片位置,变小图 hover鼠标样式 */
.card:hover .photo {
top: 30px;
width: 120px;
height: 120px;
/* 去除边框的锐度,50%是圆形 */
border-radius: 50%;
/* 设置边框的阴影凹陷 */
box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
}
- 再设置鼠标移动到图片上时,标题 h1 显示在小图片的下面,这个需要多方调试测试,达到效果。
/* 鼠标移动到图片上时,标题h1显示的内容上移 top 170px */
.card:hover h1{
top:170px;
}
- 通过伪类选择符
:.card a:hover
设置鼠标移动到,了解更多的链接时,显示的文字颜色,以及边框背景颜色变化
/* 通过伪类选择符;设置鼠标移动到,了解更多的链接时,显示的文字颜色,以及边框背景颜色变化 */
.card a:hover {
color: #fff;
background-color: rgba(255, 255, 255, 0.2);
}
具体的完整代码如下:
/* 初始化界面 */
* {
/* 上下左右外不补丁为0 */
margin: 0;
/* 上下左右内不补丁为0 */
padding: 0;
}
/* 整体布局的样式 */
body {
/* vh 是视图单位 */
height: 100vh;
/* 弹性布局,默认横向排列 */
display: flex;
/* 弹性布局水平居中 */
justify-content: center;
/* 弹性布局,上下垂直居中 */
align-items: center;
/* 背景颜色设置为渐变色 */
background: linear-gradient(200deg, #517fa4, #243949);
/* deg是CSS中的一个角度单位,表示度(Degress),一个圆共360度。在CSS中角度单位有:度(deg)、梯度(grad)、弧度(rad) */
}
/* 所有卡片的布局 */
.card {
/* 相对定位 */
position: relative;
width: 300px;
height: 500px;
/* 上下左右外补丁 */
margin: 20px;
background-color: #758a99;
/* 去除边框的锐度 */
border-radius: 20px;
/* 溢出隐藏,去除超出边界的部分 */
overflow: hidden;
/* 弹性布局,flex默认横向排列,none不显示,block以块的方式显示独占行,inline-block:以块的形式显示,并不会独占行 */
display: flex;
/* 设置元素信息纵向排列column:主轴与块轴方向作为默认的书写模式。即纵向从上往下排列(顶对齐) */
flex-direction: column;
/* 上下垂直居中 */
align-items: center;
color: #fff;
/* 设置阴影凹陷 */
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
/* 根据弹性盒子元素所设置的收缩因子作为比率来收缩空间。防止被其他盒子压缩导致图片缩小
注意:如果元素不是弹性盒对象的元素,则 flex-shrink 属性不起作用。 */
flex-shrink: 0;
}
/* 设置所有图片的样式 */
.card .photo img {
width: 100%;
height: 100%;
/* 保持图片原有的尺寸比例填充,防止失帧 */
object-fit: cover;
}
/* 设置最开始显示的大图 */
.card .photo {
/* 绝对定位 */
position: absolute;
top: 0;
width: 100%;
height: 100%;
/* 去除边框的锐度 */
border-radius: 0%;
/* 隐藏溢出的边角,隐藏超过边角的角 */
overflow: hidden;
/* 动画过渡 设置加载延迟时间*/
transition: 0.5s;
}
/* 通过伪类选择符:hover,设置鼠标移入到图片位置,变小图 hover鼠标样式 */
.card:hover .photo {
top: 30px;
width: 120px;
height: 120px;
/* 去除边框的锐度,50%是圆形 */
border-radius: 50%;
/* 设置边框的阴影凹陷 */
box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
}
/* 这里加个黑色到透明的渐变背景,可以更好的看清除名字 */
.card .photo::before{
content: "";
/* 绝对定位 */
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(to bottom,transparent);
}
/* 设置标题1 显示的文字样式 */
.card h1{
position: absolute;
top: 370px;
/* margin-left: 120px; */
/* 设置加载延迟时间 */
transition: 0.5s;
}
/* 鼠标移动到图片上时,标题h1显示的内容上移 top 170px */
.card:hover h1{
top:170px;
}
/* 设置副标题的文字样式 */
.card h2{
/* 上打外补丁 */
margin-top: 220px;
width: 80%;
/* 设置边框的样式: 边框的厚度,边框的样式(虚实线),颜色 */
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
/* 字体大小 */
font-size: 20px;
/* 文本居中 */
text-align: center;
/* 向底部,打外补丁 控制间距*/
margin-bottom: 20px;
/* 向底部,打内补丁,控制间距 */
padding-bottom: 20px;
}
.card p {
width: 90%;
/* 文本左边缩进 */
text-indent: 20px;
/* 字体大小 */
font-size: 15px;
/* 底部,打外补丁 */
margin-bottom: 15px;
/* 文本行与行之间的间距 */
line-height: 30px;
}
.card a{
/* 字体大小 */
font-size: 14px;
/* 字体背景颜色 */
color: rgba(255, 255, 255, 0.8);
/* 去除链接的下划线 */
text-decoration: none;
/* 设置边框的样式: 边框的厚度,边框的样式(虚实线),颜色 */
border: 1px solid rgba(255, 255, 255, 0.5);
/* 两个值:上下,左右内补丁 */
padding: 8px 32px;
/* 边框的锐度 */
border-radius: 8px;
}
/* 通过伪类选择符;设置鼠标移动到,了解更多的链接时,显示的文字颜色,以及边框背景颜色变化 */
.card a:hover {
color: #fff;
background-color: rgba(255, 255, 255, 0.2);
}
三. 快递包邮界面的实现
这个快递包邮,我是仿写的,是一个 枫叶动画 。是一个动态的。
1. 页面元素和内容部分:HTML
同样我们需要,也是从整体到局部,一点一点实现细节
- 定义三个盒子,每个盒子中定义四个div ,四个div 存放四个不同的枫叶。
- 三个盒子中的类名都是一致的,处理一个盒子的样式,就可以其他都是一样的了。
- 需要注意的是,这里我们使用了,同一个标签,定义两个类名。用于处理枫叶的动画显示。
具体代码实现如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>枫叶动画</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- 页眉 -->
<section>
<h3>Hello World</h3>
<!-- 先进行一个整体的布局,三个div盒子,每个div盒子放8个枫叶-->
<div class="set set1">
<div><img src="./img/leaves1.png" alt=""></div>
<div><img src="./img/leaves2.png" alt=""></div>
<div><img src="./img/leaves3.png" alt=""></div>
<div><img src="./img/leaves4.png" alt=""></div>
</div>
<div class="set set2">
<div><img src="./img/leaves1.png" alt=""></div>
<div><img src="./img/leaves2.png" alt=""></div>
<div><img src="./img/leaves3.png" alt=""></div>
<div><img src="./img/leaves4.png" alt=""></div>
</div>
<div class="set set3">
<div><img src="./img/leaves1.png" alt=""></div>
<div><img src="./img/leaves2.png" alt=""></div>
<div><img src="./img/leaves3.png" alt=""></div>
<div><img src="./img/leaves4.png" alt=""></div>
</div>
</section>
</body>
</html>
2. 页面元素的外观和位置布局样式:CSS
先初始化 css样式
* {
/* 一个数值外补丁,上下左右四个位置的值 */
margin: 0;
/* 一个数值内补丁,上下左右四个位置的值 */
padding: 0;
/* 标准盒子样式 */
box-sizing: border-box;
/* font-family: "Poppins",sans-serif; */
}
具体实现代码如下:
/* 页面的初始化加载 */
* {
/* 一个数值外补丁,上下左右四个位置的值 */
margin: 0;
/* 一个数值内补丁,上下左右四个位置的值 */
padding: 0;
/* 标准盒子样式 */
box-sizing: border-box;
/* font-family: "Poppins",sans-serif; */
}
/* 设置三个枫叶整体布局位置的样式 */
section {
/* 相对定位 */
position: relative;
width: 100%;
height: 100vh;
/* 设置渐变背景 */
background: radial-gradient(#333, #000);
/*隐藏溢出的画面,hidden不显示超过对象尺寸的内容*/
overflow: hidden;
/* 弹性布局,横向排列 */
display: flex;
/* 水平居中 */
justify-content: center;
/* 上下垂直居中 */
align-content: center;
/* vh是CSS3中的相对长度单位,表示相对视口高度(Viewport Height),视口被均分为100单位的vh,即1vh = 1% * 视口高度。可以用来解决主体内容不足以撑起视口的剩余高度时,页面底部留白太多的尴尬问题。
vh是viewpoint的缩写,是根据视口的大小而改变的相对单位
1vh是占视口高度的百分之一
1wh是占视口宽度的百分之一
50wh就是盒子宽度占视口宽度的百分之五十 */
}
/* 字体样式 */
section h3 {
color: #fff;
font-size: 4em;
/* 文字居中,上下左右:外补丁自适应 */
margin: auto;
/* 设置每个中文字,英文单词间的距离 */
word-spacing: 30px;
/* 设置每个文字,每个字母之间的距离 */
letter-spacing:10px;
}
/* 设置每个枫叶div盒子的基本格式 */
section .set {
/* 绝对定位 */
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
/* 不会被鼠标点击影响到,没有也没有太大的影响 */
pointer-events: none;
}
/* 单独设置第二个枫叶div盒子样式 */
.set2 {
transform: scale(2) rotateY(180deg);
/* 透明过滤器 */
filter: blur(2px);
/* transform: 2D样式:scale(): 指定对象的2D scale(2D缩放)。第一个参数对应X轴,第二个参数对应Y轴。如果第二个参数未提供,则默认取第一个参数的值 rotate(): 指定对象的2D rotation(2D旋转),需先有 <' transform-origin '> 属性的定义 */
}
/* 单独对第二个枫叶div盒子设置样式 */
.set3 {
transform: scale(0.8) rotateX(180deg);
filter: blur(4px);
}
/* 对枫叶图片外部的边框div盒子设置样式 */
section .set div {
/* 绝对定位 */
position: absolute;
/*弹性布局以块的形式布局,并且是每个块是独占行的*/
display: block;
}
/* 对每个单独的枫叶盒子做动画处理 ,子节点*/
/* nth-child伪类选择符: (匹配父元素的第n个子元素E,假设该子元素不是E,则选择符无效。 */
section .set div:nth-child(1) {
left: 20%;
/* animation:复合属性。检索或设置对象所应用的动画特效。 */
animation: animate 15s linear infinite;
/* animation-delay 检索或设置对象动画延迟的时间 */
animation-delay: -7s;
}
section .set div:nth-child(2) {
left: 50%;
animation: animate 20s linear infinite;
animation-delay: -5s;
}
section .set div:nth-child(3) {
left: 70%;
animation: animate 20s linear infinite;
animation-delay: 0s;
}
section .set div:nth-child(4) {
left: 0%;
animation: animate 15s linear infinite;
animation-delay: -5s;
}
section .set div:nth-child(5) {
left: 85%;
animation: animate 18s linear infinite;
animation-delay: -10s;
}
section .set div:nth-child(6) {
left: 0%;
animation: animate 12s linear infinite;
animation-delay: 0s;
}
section .set div:nth-child(7) {
left: 15%;
animation: animate 14s linear infinite;
animation-delay: 0s;
}
section .set div:nth-child(8) {
left: 90%;
animation: animate 18s linear infinite;
animation-delay: -1s;
}
/* 处理伪类 定义动画 */
@keyframes animate {
0% {
opacity: 0;
top: -10%;
transform: translateX(20px) rotate(0deg);
}
10% {
opacity: 1;
}
20% {
transform: translateX(-20px) rotate(45deg);
}
40% {
transform: translateX(-20px) rotate(90deg);
}
60% {
transform: translateX(20px) rotate(180deg);
}
80% {
transform: translateX(-20px) rotate(180deg);
}
100% {
top: 110%;
transform: translateX(-20px) rotate(226deg);
}
/* transform: 2D样式:scale(): 指定对象的2D scale(2D缩放)。第一个参数对应X轴,第二个参数对应Y轴。如果第二个参数未提供,则默认取第一个参数的值 rotate(): 指定对象的2D rotation(2D旋转),需先有 <' transform-origin '> 属性的定义
deg是CSS中的一个角度单位,表示度(Degress),一个圆共360度,在CSS中角度单位有:度(deg)、梯度(grad)、弧度(rad)。无论如何声明,这些值都会解释为0~360范围内的度数*/
}
四. 登录购餐界面的实现
1. 页面元素和内容部分:HTML
这个界面的内容部分是比较简单的。
- 定义有个大 的
div
用于登录表单的边框大小,背景的设计 - 其他所有的表单上的内容,用对应的
input
标签规定了用户可以在其中输入数据的输入字段 - 使用上
placeholder
属性能够让你在文本框里显示提示信息,一旦你在文本框里输入了什么信息,提示信息就会隐藏。你以前可能无数次看到这种效果,但那些大部分是用JavaScript里实现的,而现在,HTML5提供了原生支持,而且效果更好!需要注意的是IE6、IE8是不支持的 required
的属性是一个布尔属性,规定必需在提交之前填写输入字段,如果使用该属性,则该文本框中的内容必填(或必选)的。如果没有填写或(选择的话),是会给予提示的。注释:required 属性适用于以下 类型:text, search, url, telephone, email, password, date pickers, number, checkbox, radio 以及 file
。- 必须要用form标签把代码包裹起来才有效
- 点击的按钮必须要是
submit
类型,类型为button
无效
具体代码实现如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录加注册</title>
<!-- 导入css样式 -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>登录</h1>
<h3>输入你的账号和密码</h3>
<form action="../订餐/cart.html">
<div class="form-group">
<input type="text" class="form-control" placeholder="用户名" required>
<!--required 表示必须填写-->
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="密码" required>
</div>
<div class="from-group">
<input type="checkbox">
<span class="chk-box">记住密码</span>
</div>
<div class="from-group">
<input type="submit" value="登录" class="submit">
</div>
<div class="sign-up">
<div>没有账户 ?</div>
<a href="../注册/index.html">注册</a>
</div>
</form>
</div>
</body>
</html>
2. 页面元素的外观和位置布局样式:CSS
- 初始化 css样式
- 设置背景图片
对应背景图片的一个设计我们,这里使用了 background-image: url(),url()
两个url 图片的加载。因为这里我在加载图片.png
的时候,发现存在一个图片加载比较缓慢的原因, 所以我们就使用了两个 url()同一张图片,不同格式的图片进行一个叠加的效果。
日常项目中经常会用到全屏的图片,.png
,.jgp
的图片都太大,加载缓慢,我们可以使用 webp
格式的图片或者分辨率较低的压缩图,再叠加一层清晰的 png 图片,实现快速显示的效果,叠加 png图片的目的是防止某些浏览器不是支持 webp 格式,如下代码:
background-image: url("./login.wedp"), url("./login.png");
两张图进行叠加,先显示 webp 格式,再显示 png格式的图片,因为 webp 图片会比 png 小很多,可以快速显示的效果。
WebP: 是谷歌开发的一种新图片格式,它是支持有损和无损两种压缩方式的使用直接色的点阵图。使用 webp 格式的最大优点是是,在相同质量的文件下,它拥有更小的文件体积。因此它非常适合于网络图片的传输,用于web项目,因为图片体积的减少,意味着请求时间的减少,这样会提高用户的体验。这是谷歌开发的一种新的图片格式
background-repeat: no-repeat
图片不平铺,平铺会填充整个页面,设置为 no-repeat 背景图片仅显示一次
其他参数取值:
repeat-x: 背景图像在横向上平铺
repeat-y: 背景图像在纵向上平铺
repeat: 背景图像在横向和纵向平铺
no-repeat: 背景图像不平铺
round: 当背景图像不能以整数次平铺时,会根据情况缩放图像。(CSS3)
space: 当背景图像不能以整数次平铺时,会用空白间隙填充在图像周围。(CSS3)
background-size: cover
按照屏幕大小计算尺寸 coner:将背景图片等比缩放到完全覆盖容器,背景图像有可能超出容器
其他参数取值:
用长度值指定背景图像大小。不允许负值。
用百分比指定背景图像大小。不允许负值。
auto: 背景图像的真实大小。
cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器。
contain: 将背景图像等比缩放到宽度或高度与容器的宽度或高度相等,背景图像始终被包含在容器内。
background-attachment: fixed;
背景图像固定,不会跟着鼠标滚动 ,从而填充整个页面
其他参数取值
scroll:背景图片附着在背景容器上,它是相对于背景容器(元素)固定,它会随着背景容器的滚动,而不是随着它的内容滚动(也可以看成是对元素边框固定)。
fixed: 背景图片附着在浏览器的可视区域。因为浏览器的可视区域不具备滚动的性格,所以它是不滚动的。
local:这是CSS3新增的属性值。背景图片附着在背景容器的内容区域。它会随着内容的滚动而滚动。
body {
/* 背景图片*/
/* background-image: url("./login.jpg"); */
background-image: url("./login.wedp"), url("./login.png");
/*no-repeat 图片不平铺,平铺会填充整个页面,背景图片仅显示一次*/
background-repeat: no-repeat;
/* 按照屏幕大小计算尺寸cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器 */
background-size: cover;
/*fixed :背景图像固定,不会跟着鼠标滚动 ,从而填充整个页面*/
background-attachment: fixed;
}
- 设置 from 表单的背景,边框,居中显示,阴影凹陷
background: rgba(186, 221, 253, 0.7);
表单的背景颜色 rgba(red,green,blue,最后一个是背景的透明度(值越小越透明))
实现背景颜色的透明度,还可以使用: opacity: xxx
属性参数的不透明度是以数字表示的,从0.0到1.0,完全透明是0.0,完全不透明是1.0,数字越大代表元素越不透明。
通过绝对定位,外加向外打补丁,实现表单居中显示
/* 绝对定位,控制表单的位置:居中 */
position: absolute;
top: 50%;
left: 50%;
/* 外补丁顶部缩小一点 */
margin-top: -200px;
/* 外补丁的左边缩小一点 */
margin-left: -200px;
设置表单边框外部的阴影凹陷,
box-shadow
设置阴影凹陷:水平偏移,垂直偏移,模糊值半径,阴影外延值(可以不提供),阴影颜色
box-shadow: 5px 1px 8px #BADDFD;
.container {
width: 350px;
height: 350px;
/* 表单的背景颜色 rgba(red,green,blue,最后一个是背景的透明度(值越小越透明))*/
background: rgba(186, 221, 253, 0.7);
/* 或者使用:opacity: xxx 属性参数的不透明度是以数字表示的,从0.0到1.0,完全透明是0.0,完全不透明是1.0,数字越大代表元素越不透明。 */
/* 绝对定位,控制表单的位置:居中 */
position: absolute;
top: 50%;
left: 50%;
/* 外补丁顶部缩小一点 */
margin-top: -200px;
/* 外补丁的左边缩小一点 */
margin-left: -200px;
/* 字体颜色 */
color: black;
/* 边框变圆润 */
border-radius: 25px;
/* 设置阴影凹陷:水平偏移,垂直偏移,模糊值半径,阴影外延值(可以不提供),阴影颜色 */
box-shadow: 5px 1px 8px #BADDFD;
/*fixed :背景图像固定,不会跟着鼠标滚动 ,从而填充整个页面*/
background-attachment: fixed;
/* 内边距,其实就是内补丁 */
padding: 60px;
/* 加载速度 */
transition: 0.5s;
}
- 设置表单中的input样式
background-color: transparent:
表示背景颜色是透明,一般情况下元素背景颜色默认值是transparent(透明)
让表单中的 input 文本框中的边框只显示 下面的边框线的 css处理样式
/*先让 input 文本框中的所有边框都不显示出来*/
border: none; /* none去除边框设置 *
/*再单独设置 input 文本框下面的边框显示出来,以及显示的格式*/
border-bottom: 1px solid rgba(24, 12, 12, 0.651); /* 设置用户名和密码下边框的样式 */
outline: none;
去除我们点击 input 文本框时,显示的一个边框
/* 设置表单中的input样式 */
.container .form-group .form-control {
/* none去除边框设置 */
border: none;
/* 背景颜色设置为透明 */
background-color: transparent;
/* 设置用户名和密码下边框的样式 */
border-bottom: 1px solid rgba(24, 12, 12, 0.651);
/* 去除点击边框是提示的边框 */
outline: none;
/* 上内打补丁,控制间距 */
padding-top: 10px;
width: 100%;
}
- 设置 submit 按钮的背景图片为一个渐变色 样式,通过
linear-gradient()
可以设置渐变色,其中一些好看的渐变色的参数,大家可以去百度找找看
background-image: linear-gradient(45deg, #feac5e, #c779d0, #4bc0c8);
background: linear-gradient(to right,#feac5e,#c779d0,#4bc0c8);
background: linear-gradient(to right,#d3959b,#bfe6ba);
background-image: linear-gradient(45deg, #0081ff, #1cbbb4);
.submit {
/* 边框设置为 0 */
border: 0;
/* 设置背景颜色为渐变色 */
background-image: linear-gradient(45deg, #feac5e, #c779d0, #4bc0c8);
width: 80%;
height: 30px;
/* 外上补丁,控制边距 */
margin-top: 30px;
/* 字体大小 */
font-size: 17px;
/* 字体颜色 */
color: black;
/* 设置加载速度 */
transition: 0.5s;
}
- 通过伪类选择符
: hover
设置鼠标经过 submit 按钮,变化边框背景颜色,同样是一个渐变色,以及鼠标变成小手。
/* 鼠标经过,登入变化小手,以及文字颜色以及边框颜色 */
.submit:hover {
color: black;
cursor: pointer;
/* 设置背景图片为渐变色 */
background-image: linear-gradient(45deg, #0081ff, #1cbbb4);
}
- 最后通过
text-decoration: none;
可以去除 跳转到注册 的一个链接的下划线
完整的代码如下
*{
padding: 0;
margin: 0;
}
body {
/* 背景图片*/
/* background-image: url("./login.jpg"); */
background-image: url("./login.wedp"), url("./login.png");
/*no-repeat 图片不平铺,平铺会填充整个页面,背景图片仅显示一次*/
background-repeat: no-repeat;
/* 按照屏幕大小计算尺寸cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器 */
background-size: cover;
/*fixed :背景图像固定,不会跟着鼠标滚动 ,从而填充整个页面*/
background-attachment: fixed;
/*
日常项目中经常会用到全屏的图片, .png 和 .jpg 的图片都太大,加载缓慢。
我们可以使用webP格式的图片或者分辨率较低的压缩图,再叠加一层清晰的 png 图片,实现快速显示的效果。叠加 png 图片的目的是防止某些浏览器不支持 webP格式
如下代码:background-image: url("./login.wedp"), url("./login.png"):两张图会进行叠加,先显示 webP 格式,再显示 png 格式,因为 webP 图片会比 png 小很多,可以实现快速显示的效果 */
}
.container {
width: 350px;
height: 350px;
/* 表单的背景颜色 rgba(red,green,blue,最后一个是背景的透明度(值越小越透明))*/
background: rgba(186, 221, 253, 0.7);
/* 或者使用:opacity: xxx 属性参数的不透明度是以数字表示的,从0.0到1.0,完全透明是0.0,完全不透明是1.0,数字越大代表元素越不透明。 */
/* 绝对定位,控制表单的位置:居中 */
position: absolute;
top: 50%;
left: 50%;
/* 外补丁顶部缩小一点 */
margin-top: -200px;
/* 外补丁的左边缩小一点 */
margin-left: -200px;
/* 字体颜色 */
color: black;
/* 边框变圆润 */
border-radius: 25px;
/* 设置阴影凹陷:水平偏移,垂直偏移,模糊值半径,阴影外延值(可以不提供),阴影颜色 */
box-shadow: 5px 1px 8px #BADDFD;
/*fixed :背景图像固定,不会跟着鼠标滚动 ,从而填充整个页面*/
background-attachment: fixed;
/* 内边距,其实就是内补丁 */
padding: 60px;
/* 加载速度 */
transition: 0.5s;
/* margin: auto | length ;
如果提供全部四个参数值,将按上-右-下-左的顺序作用于四边。
如果只提供一个,将用于全部的四边。
如果提供两个,第一个用于上-下,第二个用于左-右。
如果提供三个,第一个用于上,第二个用于左-右,第三个用于下
auto : 值被设置为相对边的值 */
}
/* 设置登录字体样式 */
.container h1 {
/* 文本居中 */
text-align: center;
/* 上下外补丁,控制间距 */
margin-top: 10px;
margin-bottom: 10px;
/* 登录两字的间距 ,设置*/
letter-spacing: 10px;
/* 文本颜色 */
color: #273c75;
}
/* 输入你的账号和密码的文字样式 */
.container h3 {
/* 文字居中 */
text-align: center;
/* 下外补丁,控制间距 */
margin-bottom: 15px;
/* "输入你的账号和密码"的文字之间的间距 */
letter-spacing: 2px;
/* 文字颜色 */
color: #487ed0;
}
/* 表单的上外补丁,控制间距 */
.container form {
margin-top: 35px;
margin-left: 80px;
}
/* 设置表单中的input样式 */
.container .form-group {
/* 下外补丁,控制间距 */
margin-bottom: 10px;
width: 80%;
}
/* 设置表单中的input样式 */
.container .form-group .form-control {
/* none去除边框设置 */
border: none;
/* 背景颜色设置为透明 */
background-color: transparent;
/* 设置用户名和密码下边框的样式 */
border-bottom: 1px solid rgba(24, 12, 12, 0.651);
/* 去除点击边框是提示的边框 */
outline: none;
/* 上内打补丁,控制间距 */
padding-top: 10px;
width: 100%;
}
.submit {
/* 边框设置为 0 */
border: 0;
/* 设置背景颜色为渐变色 */
background-image: linear-gradient(45deg, #feac5e, #c779d0, #4bc0c8);
width: 80%;
height: 30px;
/* 外上补丁,控制边距 */
margin-top: 30px;
/* 字体大小 */
font-size: 17px;
/* 字体颜色 */
color: black;
/* 设置加载速度 */
transition: 0.5s;
}
/* 鼠标经过,登入变化小手,以及文字颜色以及边框颜色 */
.submit:hover {
color: black;
cursor: pointer;
/* 设置背景图片为渐变色 */
background-image: linear-gradient(45deg, #0081ff, #1cbbb4);
}
/* 注册样式设置,控制行间距 */
.container .sign-up {
/* 设置加载速度 */
transition: 0.5;
font-size: 17px;
margin-left: 2px;
}
.container a {
/* 去掉连接的下滑线 */
text-decoration: none;
/* 文字颜色 */
color: blue;
}
五. 注册界面的实现
1. 页面元素和内容部分:HTML
- 这个注册表的页面元素和内容部分还是比较少的
- 定义一个外大的div用于设置页面背景图,再定义一个内 div 用于设置 from 表单的背景以及位置
- 再定义 span 用于显示处理,用户输入错误的文字提示内容,注意 span 与 div 的区别
span 是不会独占行的,div 会独占行显示
- 注意为,用户名,密码,确认密码,邮箱设置,
name,id
用于 javasciprt 的事件的用户输入处理。还有一点就是 如果没有定义 name是无法将表单中的数据提交给服务器的,因为 https 的协议规定了。
表单时以什么格式提交将数据提交给服务器的
提交的格式是 :
action(URL)name=value&name=value&name=value&...
,这是 W3CHTTP 协议规定的,必须以这样的格式将数据提交给服务器。所以重点:表单填写了
name
属性的数据,都会提交给服务器,而如果不想提交将该数据提交给服务器的话,就不要写name
属性了
- 因为这里我们需要对输入的密码与确认密码进行一个“是否一致的验证效果”,所以这里我们先将 密码框定义为
text
好用于我们,查看输入的密码与确认密码是否一致。 - 如果我们不想让
button
普通按钮提交数据的话,可以定义点击事件为onclick="return false"
<button onclick="return false">发送验证码</button>
具体完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录注册</title>
<!-- 导入css文件 -->
<link rel="stylesheet" href="style.css">
<!-- 导入javascript文件 -->
<script src="./signIn.js" type="text/javascript"></script>
</head>
<body>
<div class="container" id="container">
<div class="form-container">
<!--表单注册-->
<form>
<h1>用户注册</h1>
<input type="text" name="username" id="username" placeholder="用户名" required />
<span id="usenameError"></span>
<input type="text" name="userpwd" id="userpwd" placeholder="密码" required>
<span id="userpwdError"></span>
<input type="text" name="userpwd2" id="userpwd2" placeholder="确认密码" required>
<span id="userpwd2Error"></span>
<input type="text" name="email" id="email" placeholder="邮箱" required>
<span id="emailError"></span>
<button onclick="return false">发送验证码</button>
<input type="text" placeholder="验证码" required>
<button id="submitBtn">注册</button>
</form>
</div>
</div>
</body>
</html>
2. 页面元素的外观和位置布局样式:CSS
- 同样为了考虑上,浏览器的兼容性,率先初始化 css 样式
- 定义整体布局样式 body
font-family:
可以设置字体的样式,可以为 “宋体,黑体”等等,这个下面的字体格式,我是从百度上找到的,一个用户注册的字体样式。
/* 设置整体布局样式 */
body {
/* 字体样式 */
font-family: Arial, Helvetica, sans-serif;
background-image: url(./reception4.jpg);
/* background-image: url(./reception5.jpeg); */
/*no-repeat 图片不平铺,平铺会填充整个页面*/
background-repeat: no-repeat;
/* 按照屏幕大小计算尺寸cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器 */
background-size: cover;
/*fixed :背景图像固定,不会跟着鼠标滚动 ,从而填充整个页面*/
background-attachment: fixed;
}
- 使用绝对定位处理,外加一些 外打补丁,让表单整体居中
.container {
/* 相对定位,让表单居中显示 */
position: absolute;
top: 50%;
left: 50%;
margin-left: -200px;
margin-top: -250px;
}
- 设置 input 的内容上的样式处理
使用text-indent: 2.2rem;
可以设置 input 框中的 placeholder 显示的内容上的文字的一个文本左边缩进的大小。
font-size: 0.6rem;
可以设置字体的大小。
rem:全称root em,是一种相对单位
。与em类似,em也是相对单位,em相对的是元素的父级元素字体大小来说的。rem是相对与html元素字体大小来说的,这也就很好理解为什么是root em了,因为html是跟元素啊,这也就好理解了。使用rem跟媒体查询结合的话,可以实现设备屏幕变化,页面显示也随着变化,从而达到适配不同手机屏幕的效果。那知道了相对于谁,接下来该考虑如何计算元素的rem了。
这里有个公式 页面元素的rem值 = 页面元素的px / html的字体大小
html字体大小 = 设计稿的宽度 / 分成的等分(15或者10或者其他)
单位需要注意的问题
a. 若两个数值运算时,有一个数值带单位,计算结果就是该单位; 100px/50 = 2px
b. 若两个数值运算时,两个数值都有单位,计算结果的单位是 带rem的; 100rem / 50px = 2rem 或者 100px / 50rem = 2rem
一般的写法:第一个数值带rem的单位,另一个数值不带单位。标准的话:100rem / 50 = 2rem
em同理。
边框只显示下面的边框线,上左右的边框线消失
/* 上左右边框线消失,保留显示右下边框横线 */
border-left: none;
border-top: none;
border-right: none;
/*最后显示只显示下面的边框横线,并设置边框线的样式*/
/* 边框样式:边框大小,边框虚实线,边框颜色 */
border: 6px solid #ccc;
outline: none;
去除我们点击 input 文本框时,显示的一个边框
/* 点击时input 的边框消失 */
outline: none;
.form-container input {
width: 100%;
/* height: 0px; */
height: 2.2rem;
background-color: #bdc3c7;
/* 文本左边缩进 */
text-indent: 2.2rem;
/* 边框样式:边框大小,边框虚实线,边框颜色 */
border: 6px solid #ccc;
/* 上左右边框线消失,保留显示右下边框横线 */
border-left: none;
border-top: none;
border-right: none;
/* 点击时input 的边框消失 */
outline: none;
margin: 0.6rem 0;
}
- 通过伪类选择符
:active
对 button 按钮点击住 时,有一个颜色的变化,一个渐变颜色的变化如下
/* 鼠标点击选中时,让按钮有一个渐变的效果的变化 */
.form-container button:active {
background-image: linear-gradient(45deg, #feac5e, #c779d0, #4bc0c8);
}
完整代码:
/* 界面初始化 */
* {
margin: 0;
padding: 0;
/* 标准盒子 */
box-sizing: border-box;
}
/* 设置整体布局样式 */
body {
/* 字体样式 */
font-family: Arial, Helvetica, sans-serif;
background-image: url(./reception4.jpg);
/* background-image: url(./reception5.jpeg); */
/*no-repeat 图片不平铺,平铺会填充整个页面*/
background-repeat: no-repeat;
/* 按照屏幕大小计算尺寸cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器 */
background-size: cover;
/*fixed :背景图像固定,不会跟着鼠标滚动 ,从而填充整个页面*/
background-attachment: fixed;
}
.container {
/* 相对定位,让表单居中显示 */
position: absolute;
top: 50%;
left: 50%;
margin-left: -200px;
margin-top: -250px;
/* 边框锐度,50%为圆*/
/* 阴影布局 */
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25);
width: 500px;
height: 500px;
}
.form-container form {
background: #bdc3c7;
padding-top: 20px;
/* 边框的锐度,50%是圆 */
border-radius: 20px;
/* 背景的透明度 */
opacity:0.8;
/* 弹性布局 flex 横向布局,不会独占行 */
display: flex;
flex-direction: column;
/* padding: 0 1.8rem; */
height: 100%;
/* 水平居中 */
justify-content: center;
/* 垂直居中 */
align-items: center;
/* flex-direction属性决定主轴的方向(即项目的排列方向)
row(默认值):主轴为水平方向,起点在左端。
row-reverse:主轴为水平方向,起点在右端。
column:主轴为垂直方向,起点在上沿。
column-reverse:主轴为垂直方向,起点在下沿。 */
}
.form-container input {
width: 100%;
/* height: 0px; */
height: 2.2rem;
background-color: #bdc3c7;
/* 文本左边缩进 */
text-indent: 2.2rem;
/* 边框样式:边框大小,边框虚实线,边框颜色 */
border: 6px solid #ccc;
/* 上左右边框线消失,保留显示右下边框横线 */
border-left: none;
border-top: none;
border-right: none;
/* 点击时input 的边框消失 */
outline: none;
margin: 0.6rem 0;
}
/* 鼠标点击选中时,让按钮有一个渐变的效果的变化 */
.form-container button:active {
background-image: linear-gradient(45deg, #feac5e, #c779d0, #4bc0c8);
}
.form-container button {
/* 内补丁,左右,上下, */
padding: 1rem 0.5rem;
background: #417dff;
color: white;
/* 边框 */
border: 1px solid #fff;
/* 鼠标点击时,边框消失 */
outline: none;
/* 鼠标经过,小手 */
cursor: pointer;
width: 5rem;
/* 设置边框锐度,50%是圆 */
border-radius: 8px;
/* 加载延迟时间 */
transition: all 1000ms ease-in;
margin: 0.6rem 0;
/* 字体大小 */
font-size: 0.6rem;
}
span{
position: relative;
width: 80%;
height: 20px;
/* 给块设置文本居中 */
text-align:center
}
3. 网页模型的定义与页面交互:JavaScript
这里我们需要编写 javaScript 代码用于实现对注册时,用户输入的内容的一个校验。
具体校验内容如下:
- 用户名不为空
- 用户名必须在 6 -14之间
- 用户名只能由数字和字母组成不能含有其它符号
- 密码不为空以及和确认密码一致才行
- 邮箱不为空,邮箱合理性:正则表达式
- 错误提示信息,统一在span 标签中提示,并且要求字体 12 号
- 统一失去焦点事件,验证
- 当文本框再次获得焦点后,清除错误提示信息,以及如果文本框中的数据不合法,要求清空文本框的value内容
- 最后,只有当表单中所有项均合法,方可提交,数据给服务器。
基本上所有的注册都是,使用失去焦事件onfocus
验证的
- 首先我们这里使用一个
window.onload
的方式定义一个统一的匿名函数,该函数的存放所有的注册文本框的绑定的校验的事件的函数。如下图所示。只有当页面所有的数据加载完了,才会触发该事件,调用其中的所有对应事件绑定的匿名函数
好处: 就是在HTML代码中不会出现,有关 javaScript 的代码以及函数的调用的编写,但是却在 js 代码中实现了,相关的函数调用,保证了一个,不同的层面处理不同的功能的,一个清晰的划分。正因为我们在 html中没有调用js中的函数,所有就存在一个问题,就是 js 获取 html对应标签的 id
时存在一个问题,就是当我们html 中的 id 并没有加载完毕,而却想 js 通过这个未加载完毕的 id 获取到对应 id 的对象,这是不行的,因为这时候的 id 并没有加载到页面当中去,js 是无法获取到这个不存在于页面上的 id 的对象的。 通过 onload
改变js的执行顺序,获取已经加载的id的对象。
所以就需要 window.onload
只有当页面中所有的元素都加载完毕了,才会触发该事件,执行调用这个匿名函数,就不存在一个 id 未加载到页面当中的问题了。这样我们就可以通过 js 代码获取到对应的 对象,让对象进行一个绑定注册上对应的事件以及匿名函数。用于处理对用户输入的内容进行一个校验了。
对于 js 代码的编写有一个小技巧:F12
打开我们的浏览器调试控制台窗口,当我们编写的 js 代码 运行时,存在错误时,浏览器会及时的提示我们错误,便于我们对 bug 及时做出修改 。以及对于 id 获取到的对象是否成功,数据的是否合理,可以通过console.log()
将参数信息打印显示到 浏览器的控制台上,或者使用window.alert()
在弹窗上显示参数信息。
- 判断用户名的合理性
-
首先我们需要使用 js 内置的对象
document.getElementById()
通过 id 获取到关于用户名的 input 文本框的对象,以及对应用户名错误提示的 span标签对象(用于改变错误提示信息内容),获取到以后我们需要使用console.log()
在浏览器控制台中,打印显示我们获取的对象是否存在,是否合理。 -
获取到用户名文本框的对象以后,为该用户名对象绑定注册上
usernameElt.onblur = function ()
失去焦点事件,并定义匿名函数 ,该函数就是用来处理对用户名的校验的。当触发失去焦点事件,执行该匿名函数。 -
我们需要通过获取到用户名对象
usernameElt.value
点出,用户输入的value
值,从而对用户输入的值进行一个验证。同样使用console.log()
在浏览器控制台中,打印显示我们获取的 value值是否,是我们用户输入的值。获取到值以后,我们就需要对用户输入的内容进行一个校验了。-
用户的输入不能为空
通过一个方法:
username.trim()
去除字符串左右两边的空格,中间的保留 ,去除空格以后,再判断用户的输入是否为空,三种方式:1.if (username == "")
返回 true 表示用户输入的是空,2.if(username)
true表示用户输入的是空,3.if(username.length == 0)
true 表示用户输入的是空。 当用户输入的是 空时,通过我们前面获取到的用户错误提示的 span 对象userNameError.innerText = "用户名不能为空"
获取到其中的 innerText 的属性,可以改变我们 span 错误提示显示的内容信息,这个改为 “用户名不能为空”。同样可以使用另外一个属性userNameError.innerHTML
,也是可以改变 span 显示的信息内容。注意区分
innerHTML
和innerText
的区别:innerHTML: 是会将后面的字符串当做一段HTML代码解释并执行,最终展示一个效果
innerText: 仅仅只是将后面的字符串,以文本的形式打印显示出来,即使后面是一段html代码,也只是将其当做一个普通的字符串 文本来处理。
-
判断用户名的长度是否合法
用户名是在 [6~14]区间内:
if (username.length < 6 || username.length > 14) {
同样错误改变 spanuserNameError.innerText = "用户名[6~14]区间内"
显示的错误信息 -
判断用户名是否包含特殊符合
这里我们使用正则表达式:
/^[0-9A-Za-z]+$
只能是 数字 0~9,字母 a-z,A-Z。再使用方法var ok = regExp.test(username)
判断比较内容是否符合正则表达式,符合返回 true ,不符合返回false。注意这里我们将一定存在的字符串内容放到.text
的左边,不要作为参数使用,因为当我们的字符串是null 的时候,null.test
就会报错了,所以我们让左值定义存在的字符串。返回的是false 用户名不合法,修改 span 的错误提醒userNameError.innerText = "用户名只能由数字和字母组成"
-
用户名错误,当文本框再次获得焦点后,清除错误提示信息,以及如果文本框中的数据不合法,要求清空文本框的value内容
- 通过获取到的对应用户名错误提示的 span 对象,为该对象 **注册绑定上
usernameElt.onfocus = function ()
** 获得焦点事件,并定义匿名函数,当获得焦点,执行该匿名函数。 - 我们通过判断 span 错误提示是否存在内容的显示,来判断出用户输入的内容是否合法 。
- 如果 span 没有错误提示信息的内容,说明用户输入的内容是合法的 ,不用处理。
- 如果 span 存在错误提示信息的内容,说明用户输入的内容是不合法 的,处理。
- 用户输入的内容不合法:
if (userNameError.innerText != "") {
span 含有错误提醒,将用户输入到文本框中的错误的信息清空,usernameElt.value = “”,并清除 span 的错误提示 : userNameError.innerText = “”; 。- 需要注意的是 这个
userNameError.innerText = ""
一定是在 if (userNameError.innerText != “”) 这判断的后面的,如果是前面的话,用永远没有错误提示信息了,因为被你给清空空。
// 用户名span错误提示处理,通过用户文本框对象绑定上获取onfocus焦点事件处理 usernameElt.onfocus = function () { // 如果username<span>块中存在错误提示,则说明没有用户名不合法,获取焦点,清空文本框的value以及span.innerText的内容 if (userNameError.innerText != "") { // 清空username的value错误输入 usernameElt.value = "" } // 再将提示错误的span置为“” userNameError.innerText = ""; }
- 通过获取到的对应用户名错误提示的 span 对象,为该对象 **注册绑定上
-
完整用户校验的 js 代码如下:
window.onload = function () { // onload改变js的执行顺序,获取已经加载的id的对象
// 通过iid获取到用户名对象
var usernameElt = document.getElementById
("username")
// console.log(username) 测试一下
// 获取用户名后面的错误提示信息的对象
var userNameError = document.getElementById("usenameError")
// 处理用户名的验证,通过用户名对象绑定 onblur失去焦点事件,进行验证
usernameElt.onblur = function () {
// 通过对象获取到 用户名文本框的value的值
var username = usernameElt.value
// console.log(username) 测试
username = username.trim(); // trim 去除字符串两边的空格
// console.log(username) 测试
if (username == "") { // 或者直接是if(username)或者是if(username.length == 0)
// 用户不能为空
userNameError.innerText = "用户名不能为空"
// innerText 设置赋值span块中显示的内容
} else {
// 判断用户名的长度是否合法
if (username.length < 6 || username.length > 14) {
userNameError.innerText = "用户名[6~14]区间内"
} else {
// 判断用户名是否包含特殊符合,
var regExp = /^[0-9A-Za-z]+$/ //^开始$结束,{} 表示限定长度
var ok = regExp.test(username) // 与正则表达式匹配,完全匹配返回true,不匹配返回false
if (ok) {
// 用户名合法
} else {
// 用户名不合法,包含特殊字符
userNameError.innerText = "用户名只能由数字和字母组成"
}
}
}
}
// 用户名span错误提示处理,通过用户文本框对象绑定上获取onfocus焦点事件处理
usernameElt.onfocus = function () {
// 如果username<span>块中存在错误提示,则说明没有用户名不合法,获取焦点,清空文本框的value以及span.innerText的内容
if (userNameError.innerText != "") {
// 清空username的value错误输入
usernameElt.value = ""
}
// 再将提示错误的span置为“”
userNameError.innerText = "";
}
}
- 用户密码的验证的校验:密码不为空以及和确认密码一致才行
这里需要注意的是,我们需要将密码框先定义为 <input type="text"
的文本框,因为如果我们一开始就定义成了 password
的话,我们就无法获取看看我们输入的内容了,也就无法判断我们编写的 js 代码是否合理了。
-
密码不能为空:
和上面用户名校验是一样的,我们同样需要先通过
document.getElementById()
方法获取到对应的密码 id 的对象,以及 密码错误提示信息的 span id 的对象,然后为该密码对象 注册绑定上userpwdElt.onblur = function () {
失去焦点的事件以及匿名函数,由于处理密码的校验。这里判断密码是否为空的方式是和上面用户名是否为空的方式是一样的。使用userpwd = userpwd.trim();
方法,去除左右空格,保留中间的中间的空格。将左值为一定不是null的字符串。当文本框再次获得焦点后,清除错误提示信息,以及如果文本框中的数据不合法,要求清空文本框的value内容。和上面用户名的方法是一样的。
// 密码的验证
// 获取密码的对象
var userpwdElt = document.getElementById("userpwd")
// console.log(userpwd) 测试
var userpwdError = document.getElementById("userpwdError")
// console.log(userpwdError) 测试
// 给密码对象绑定上.onblut失去焦点事件: 处理密码为空的验证
userpwdElt.onblur = function () {
// 通过对象获取到密码的 value值
var userpwd = userpwdElt.value;
// console.log("--->"+userpwd+"<-----") 测试
userpwd = userpwd.trim(); // 去除左右空格
// console.log("--->"+userpwd+"<-----") 测试
if (userpwd == "") {
// 密码不能为空
userpwdError.innerText = "密码不能为空"
}
}
- 密码和确认密码保持一致:
首先通过 id 获取到对应 确认密码的对象,以及确认密码的错误提示信息,因为上面密码的对象我们已经获取到了。这里我们只需要比较 两个 value 值(用户输入的值) 一个是密码的value,另外一个是确认密码的value 的值,通过 对象.
获取,判断,如果不一致,再通过 userpwd2Error.innerText = "密码不一致"
改变 span 错误提示的内容。
// 处理确认密码的错误处理
// 确认密码的
var userpwdElt2 = document.getElementById("userpwd2")
// console.log(userpwd2) 测试
var userpwd2Error = document.getElementById("userpwd2Error")
// console.log(userpwd2Error) 测试
// 为确认密码绑定上,onblur,失去焦点验证
userpwdElt2.onblur = function () {
if (userpwdElt.value != userpwdElt2.value) {
// 两个密码不一致
userpwd2Error.innerText = "密码不一致"
}
}
关于密码和确认密码的验证完整的 js 代码如下:
// 只有当页面所有的数据加载完了,才会触发该事件,调用其中的匿名函数
window.onload = function () { // onload改变js的执行顺序,获取已经加载的id的对象
// 密码的验证
// 获取密码的对象
var userpwdElt = document.getElementById("userpwd")
// console.log(userpwd) 测试
var userpwdError = document.getElementById("userpwdError")
// console.log(userpwdError) 测试
// 给密码对象绑定上.onblut失去焦点事件: 处理密码为空的验证
userpwdElt.onblur = function () {
// 通过对象获取到密码的 value值
var userpwd = userpwdElt.value;
// console.log("--->"+userpwd+"<-----") 测试
userpwd = userpwd.trim(); // 去除左右空格
// console.log("--->"+userpwd+"<-----") 测试
if (userpwd == "") {
// 密码不能为空
userpwdError.innerText = "密码不能为空"
}
}
// 处理清除密码错误的输入,以及错误提示
// 为密码对象绑定上 onfocus 获取焦点
userpwdElt.onfocus = function () {
if (userpwdError.innerText != "") {
// span有错误提示,说明输入不合法,清空 value
userpwdElt.value = ""
}
// 清空错误提示
userpwdError.innerText = ""
}
// 处理确认密码的错误处理
// 确认密码的
var userpwdElt2 = document.getElementById("userpwd2")
// console.log(userpwd2) 测试
var userpwd2Error = document.getElementById("userpwd2Error")
// console.log(userpwd2Error) 测试
// 为确认密码绑定上,onblur,失去焦点验证
userpwdElt2.onblur = function () {
if (userpwdElt.value != userpwdElt2.value) {
// 两个密码不一致
userpwd2Error.innerText = "密码不一致"
}
}
// 处理确认密码的错误提醒
// 绑定上onfous 获取焦点事件
userpwdElt2.onfocus = function () {
if (userpwd2Error.innerText != "") {
// 存在错误提示,清空错误的输入value
userpwdElt2.value = ""
}
// 清除错误提示
userpwd2Error.innerText = ""
}
}
- 邮箱的验证处理
对于邮箱的验证,比较简单,直接使用正则表达式 ,关于一些邮箱的正则表达式,大家可以直接上百度就可以拿到了,选择一个自己认为比较合适就可以了。
同样的我们需要通过 id 获取到对应邮箱的文本框的对象,以及 span 错误的提示框对象,再为邮箱的对象 emailElt.onblur = function () {
注册绑定上失去焦点的事件,以及匿名函数,
使用 .test()
的函数,比较判断是否符合正则表达式,让一定不为null, 的字符串作为左值,防止出现 null.test()的错误。
// 邮箱的验证处理
var emailElt = document.getElementById("email")
// console.log(emailElt) 测试
var emailError = document.getElementById("emailError")
// console.log(emailError) 测试
// 为邮箱绑定上onblur失去焦点事件,
emailElt.onblur = function () {
var emailRegExp = /^[a-zA-Z0-9]+([-_.][A-Za-zd]+)*@([a-zA-Z0-9]+[-.])+[A-Za-zd]{2,5}$/ // 邮箱的正则表达式
var email = emailElt.value
var ok = emailRegExp.test(email) // 匹配返回true,否则返回false
if (ok) {
} else {
emailError.innerText = "邮箱地址不合法"
}
}
// 处理邮错误的提示
// 为邮箱对象绑定上onfocus获取焦点,处理错误的提示信息
emailElt.onfocus = function () {
if (emailError.innerText != "") {
// 存在错误提醒,输入的数据不合法,清空
emailElt.value = "";
}
// 清空span的错误提示
emailError.innerText = "";
}
4. 最后处理只有所有都合法才能提交信息
该功能是:验证我们所有项是否合法,但凡存在一个不合法项都是无法将数据提交给服务器了。因为如果你的数据都不合法了,还能将数据提交给了服务器处理了,那你上面写的哪些 js 验证的有什么意义。使用 js 的目的之一就是:将一些简单的业务处理在前端完成,减轻后端服务器的负担。
- 首先我们需要将,from 表单中的提交按钮,改为普通按钮
button
,因为如果是submit
提交按钮是一定会将数据提交给服务器的,所以这里我们将它修改成普通按钮,在通过 js 代码提交数据 - 通过 button 按钮上的 id 获取到对应的对象,再为该对象绑定上
submitElt.onclick = function () {
点击事件,以及匿名函数,当触发事件,执行该匿名函数。 - 这里我们使用 DOM编程 中的两个方法分别是
focus(),blur()
focus()
: 可以通过 js 代码触发对应的对象的获取焦点事件。blur()
: 可以通过 js 代码触发对应的对象的失去焦点事件
这样当我们击onclick注册触发点击事件,通过纯js代码,,触发对应的 username用户名和 userpwed密码以及邮箱的bulr,focus事件,不需要人工操作,使用javasprict 触发。
-
所有文本框的验证事件都触发以后,再判断所有项的
span.innerText
错误提示信息是否有内容。- 如果有错误信息内容就说明,存在不合法项,不能提交数据给服务器。并使用
window.alert()
弹窗提示有误。 - 如果所有项都没有错误提示信息,就说明所有数据都合法,可以将数据提交给服务器处理了
- 如果有错误信息内容就说明,存在不合法项,不能提交数据给服务器。并使用
-
使用 js 代码提交数据,同样是 DOM编程 中的方法
submit()
该方法是将表单中的数据提交给对应的action
url 位置的服务器。
具体代码如下:
// 只有当页面所有的数据加载完了,才会触发该事件,调用其中的匿名函数
window.onload = function () { // onload改变js的执行顺序,获取已经加载的id的对象
// 最后处理只有所有都合法才能提交信息
// 获取到button 普遍提交按钮的对象,通过id,注意form表单中的提交按钮不能是submit不然就算不合法也会提交的
var submitElt = document.getElementById("submitBtn")
// console.log(submitElt) 测试
// 为该button提交按钮对象绑定上,onclick点击事件
submitElt.onclick = function () {
/*
form 对象方法submit() 提交表单
当所有表单项都是合法的时候提交表单
*/
// 点击onclick注册触发点击事件,通过纯js代码,,触发对应的 username用户名和 userpwed密码以及邮箱的bulr,focus事件
// 不需要人工操作,使用javasprict 触发
// 用户名的
usernameElt.focus()
usernameElt.blur()
// 密码 和 确认密码
userpwdElt.focus();
userpwdElt.blur();
userpwdElt2.focus();
userpwdElt2.blur();
// 邮箱的
emailElt.focus();
emailElt.blur();
// 当所有的表项都没有 span的错误提示内容,就是所有都合法,提交数据表单
var boolUserName = userNameError.innerText == "" // 用户名
// console.log(boolUserName) 测试
var boolUserPwd = userpwdError.innerText == "" // 密码
// console.log(boolUserpwd2) 测试
var boolUserPwd2 = userpwd2Error.innerText == "" // 确认密码
// console.log(boolUserPwd2) 测试
var boolUseremail = emailError.innerText == "" // 邮箱
// console.log(boolUseremail) 测试
if (boolUserName && boolUserPwd && boolUserPwd2 && boolUseremail) {
// 通过id 获取到表单对象,再通过方法 submit()提交数据表单
var userfromElt = document.getElementById("userfrom")
// console.log(userfromElt) 测试
// 这里也可以设置actiojn =
userfromElt.action = "http://localhost:8080/js" // url统一资源定位符
userfromElt.submit(); // 提交数据表单,方法
} else {
window.alert("存在错误,请注意查看")
}
}
}
该注册界面的完整javascript代码如下:
/*
1.用户名不为空
2.用户名必须在 6 -14之间
3.用户名只能由数字和字母组成不能含有其它符合
4.密码不为空和确认密码一致,
5.邮箱不为空,邮箱合理性正则表达式
6.统一失去焦点验证
6. 错误提示信息,统一在span 标签中提示,并且要求字体 12 号
7. 文本框再次获得焦点后,清除错误提示信息,如果文本框
中的数据不合法,要求清空文本框的value内容
8.最终表单中所有项均合法,方可提交
*/
// 只有当页面所有的数据加载完了,才会触发该事件,调用其中的匿名函数
window.onload = function () { // onload改变js的执行顺序,获取已经加载的id的对象
// 通过iid获取到用户名对象
var usernameElt = document.getElementById
("username")
// console.log(username) 测试一下
// 获取用户名后面的错误提示信息的对象
var userNameError = document.getElementById("usenameError")
// 处理用户名的验证,通过用户名对象绑定 onblur失去焦点事件,进行验证
usernameElt.onblur = function () {
// 通过对象获取到 用户名文本框的value的值
var username = usernameElt.value
// console.log(username) 测试
username = username.trim(); // trim 去除字符串两边的空格
// console.log(username) 测试
if (username == "") { // 或者直接是if(username)或者是if(username.length == 0)
// 用户不能为空
userNameError.innerText = "用户名不能为空"
// innerText 设置赋值span块中显示的内容
} else {
// 判断用户名的长度是否合法
if (username.length < 6 || username.length > 14) {
userNameError.innerText = "用户名[6~14]区间内"
} else {
// 判断用户名是否包含特殊符合,
var regExp = /^[0-9A-Za-z]+$/ //^开始$结束,{} 表示限定长度
var ok = regExp.test(username) // 与正则表达式匹配,完全匹配返回true,不匹配返回false
if (ok) {
// 用户名合法
} else {
// 用户名不合法,包含特殊字符
userNameError.innerText = "用户名只能由数字和字母组成"
}
}
}
}
// 用户名span错误提示处理,通过用户文本框对象绑定上获取onfocus焦点事件处理
usernameElt.onfocus = function () {
// 如果username<span>块中存在错误提示,则说明没有用户名不合法,获取焦点,清空文本框的value以及span.innerText的内容
if (userNameError.innerText != "") {
// 清空username的value错误输入
usernameElt.value = ""
}
// 再将提示错误的span置为“”
userNameError.innerText = "";
}
// 密码的验证
// 获取密码的对象
var userpwdElt = document.getElementById("userpwd")
// console.log(userpwd) 测试
var userpwdError = document.getElementById("userpwdError")
// console.log(userpwdError) 测试
// 给密码对象绑定上.onblut失去焦点事件: 处理密码为空的验证
userpwdElt.onblur = function () {
// 通过对象获取到密码的 value值
var userpwd = userpwdElt.b nvalue;
// console.log("--->"+userpwd+"<-----") 测试
userpwd = userpwd.trim(); // 去除左右空格
// console.log("--->"+userpwd+"<-----") 测试
if (userpwd == "") {
// 密码不能为空
userpwdError.innerText = "密码不能为空"
}
}
// 处理清除密码错误的输入,以及错误提示
// 为密码对象绑定上 onfocus 获取焦点
userpwdElt.onfocus = function () {
if (userpwdError.innerText != "") {
// span有错误提示,说明输入不合法,清空 value
userpwdElt.value = ""
}
// 清空错误提示
userpwdError.innerText = ""
}
// 处理确认密码的错误处理
// 确认密码的
var userpwdElt2 = document.getElementById("userpwd2")
// console.log(userpwd2) 测试
var userpwd2Error = document.getElementById("userpwd2Error")
// console.log(userpwd2Error) 测试
// 为确认密码绑定上,onblur,失去焦点验证
userpwdElt2.onblur = function () {
if (userpwdElt.value != userpwdElt2.value) {
// 两个密码不一致
userpwd2Error.innerText = "密码不一致"
}
}
// 处理确认密码的错误提醒
// 绑定上onfous 获取焦点事件
userpwdElt2.onfocus = function () {
if (userpwd2Error.innerText != "") {
// 存在错误提示,清空错误的输入value
userpwdElt2.value = ""
}
// 清除错误提示
userpwd2Error.innerText = ""
}
// 邮箱的验证处理
var emailElt = document.getElementById("email")
// console.log(emailElt) 测试
var emailError = document.getElementById("emailError")
// console.log(emailError) 测试
// 为邮箱绑定上onblur失去焦点事件,
emailElt.onblur = function () {
var emailRegExp = /^[a-zA-Z0-9]+([-_.][A-Za-zd]+)*@([a-zA-Z0-9]+[-.])+[A-Za-zd]{2,5}$/ // 邮箱的正则表达式
var email = emailElt.value
var ok = emailRegExp.test(email) // 匹配返回true,否则返回false
if (ok) {
} else {
emailError.innerText = "邮箱地址不合法"
}
}
// 处理邮错误的提示
// 为邮箱对象绑定上onfocus获取焦点,处理错误的提示信息
emailElt.onfocus = function () {
if (emailError.innerText != "") {
// 存在错误提醒,输入的数据不合法,清空
emailElt.value = "";
}
// 清空span的错误提示
emailError.innerText = "";
}
// 最后处理只有所有都合法才能提交信息
// 获取到button 普遍提交按钮的对象,通过id,注意form表单中的提交按钮不能是submit不然就算不合法也会提交的
var submitElt = document.getElementById("submitBtn")
// console.log(submitElt) 测试
// 为该button提交按钮对象绑定上,onclick点击事件
submitElt.onclick = function () {
/*
form 对象方法submit() 提交表单
当所有表单项都是合法的时候提交表单
*/
// 点击onclick注册触发点击事件,通过纯js代码,,触发对应的 username用户名和 userpwed密码以及邮箱的bulr,focus事件
// 不需要人工操作,使用javasprict 触发
// 用户名的
usernameElt.focus()
usernameElt.blur()
// 密码 和 确认密码
userpwdElt.focus();
userpwdElt.blur();
userpwdElt2.focus();
userpwdElt2.blur();
// 邮箱的
emailElt.focus();
emailElt.blur();
// 当所有的表项都没有 span的错误提示内容,就是所有都合法,提交数据表单
var boolUserName = userNameError.innerText == "" // 用户名
// console.log(boolUserName) 测试
var boolUserPwd = userpwdError.innerText == "" // 密码
// console.log(boolUserpwd2) 测试
var boolUserPwd2 = userpwd2Error.innerText == "" // 确认密码
// console.log(boolUserPwd2) 测试
var boolUseremail = emailError.innerText == "" // 邮箱
// console.log(boolUseremail) 测试
if (boolUserName && boolUserPwd && boolUserPwd2 && boolUseremail) {
// 通过id 获取到表单对象,再通过方法 submit()提交数据表单
var userfromElt = document.getElementById("userfrom")
// console.log(userfromElt) 测试
// 这里也可以设置actiojn =
userfromElt.action = "http://localhost:8080/js" // url统一资源定位符
userfromElt.submit(); // 提交数据表单,方法
} else {
window.alert("存在错误,请注意查看")
}
}
}
六. 订餐购餐界面的实现
1. 页面元素和内容部分:HTML
编写该页面我们,从整体再到局部,一一实现,
对应不同的菜品,我们只需要定义同一个类名就可以了,因为菜品的布局元素是一样的,只是内容上参数,不同而已。所以我们只要处理好一个菜其他的也就处理好了。
需要注意的是这里我们需要对在一些特殊的标签中,定义 id
用于 javascript 的代码获取对象进行一个处理操作,
对于每一个菜品的单价,我们需要定义上对应的隐藏域 <input type="hidden"
(就是不再页面中显示,但是在页面中存在的),目的是用于javascript 根据单价计算中对应菜品的总价:如下三个隐藏域,分别是菜品1,菜品2,菜品3的单价。
具体的完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>订餐服务</title>
<!--导入css文件-->
<link rel="stylesheet" type="text/css" href="./cart.css" />
<!--导入javarscript代码-->
<script src="./cart.js" type="text/javascript"></script>
</head>
<body>
<!--从大到小,依次构图-->
<!-- 多个订单-->
<div class="shops">
<h1>订餐服务</h1>
<!--第一个订单-->
<div class="shop">
<!--订单的头-->
<div class="header">
<!--订单的全选复选框-> -->
<input type="checkbox" class="shop_checkbox" id="allCheckbox" onclick="allCheckbox(this)"/>
<!--店铺的log,这里使用span的盒子,不会独占行,以块的形式显示-->
<span class="shop_icon"></span>
<!--餐厅名称-->
<span class="shop_name">“小味鲜”</span>
<!-- 餐厅的第二个log-->
<span class="shop_icon2"> </span>
</div>
<!--所选菜品条目一-->
<div class="item">
<!--订单条目的单选复选框-->
<div><input type="checkbox" class="item_checkbox"/></div>
<!--所选菜品的图片-->
<div class="item_img"><img src="./img/foot1.jpg" class="img" alt="">
</div>
<!--菜品介绍-->
<div class="promotion">
<!--宣传语-->
<div class="promotion_content">时尚雪纺衫女长袖2020春装新款内搭显瘦衬衫高档洋气小衫上衣打底</div>
<!--三个小图标-->
<div class="three_icons">
<span></span>
<span></span>
<span></span>
</div>
</div>
<!-- sku 菜品的参数信息-->
<div class="sku">
<!--菜品的种类介绍-->
<div class="sku_color">菜品的种类: 热菜</div>
<!--菜品名-->
<div class="sku_size">菜品: xxxL</div>
</div>
<!--菜品的价格-->
<div class="price">
<!--原价 del:横线-->
<div class="price1"><del>¥ 119.80</del></div>
<!--实际活动价格-->
<div class="price2">¥ 59.90</div>
<!--定义隐藏域,用于获取该物品的单价,计算总价-->
<input type="hidden" id="price1" value="59.90" />
</div>
<!--所选菜的数量-->
<div class="num">
<span type="minux" onclick="minusProdact(this,'price1','item_total_price1')"> - </span>
<!--注意单双引号交替使用-->
<!--实际所需商品的个数-->
<!--this表示当前标签对象,onkeydown表示鼠标按下事件,注意这里使用的是onkeyup按键弹起事件,因为我们要获取到的是时时输入的数据value,而按下事件获取到的是前一次的数据value-->
<input type="text" value="1" onkeyup="modiffCount(this,'price1','item_total_price1')" />
<span type="plus" onclick="addProdact(this,'price1','item_total_price1')"> + </span>
</div>
<!--该菜的总价: 数量*单价-->
<div class="item_total_price" id="item_total_price1">¥ 59.90</div>
<!--操作-->
<div class="operation">
<!--删除,onclick点击事件-->
<div class="del" onclick="del(this)">删除</div>
</div>
</div>
<!--所选菜品的条目二-->
<div class="item">
<!--订单条目的单选复选框-->
<div><input type="checkbox" class="item_checkbox" /></div>
<!--所选菜品的图片-->
<div class="item_img"><img src="./img/foot1.jpg" class="img" alt="">
</div>
<!--菜品介绍-->
<div class="promotion">
<!--宣传语-->
<div class="promotion_content">时尚雪纺衫女长袖2020春装新款内搭显瘦衬衫高档洋气小衫上衣打底</div>
<!--三个小图标-->
<div class="three_icons">
<span></span>
<span></span>
<span></span>
</div>
</div>
<!-- sku 菜品的参数信息-->
<div class="sku">
<!--菜品的种类介绍-->
<div class="sku_color">菜品的种类: 热菜</div>
<!--菜品名-->
<div class="sku_size">菜品: xxxL</div>
</div>
<!--菜品的价格-->
<div class="price">
<!--原价 del:横线-->
<div class="price1"><del>¥ 130.00</del></div>
<!--实际活动价格-->
<div class="price2">¥ 65.00</div>
<!--定义隐藏域,用于获取该物品的单价-->
<input type="hidden" id="price2" value="65.00" />
</div>
<!--所选菜的数量-->
<!--所选商品的数量-->
<div class="num">
<span type="minux" onclick="minusProdact(this,'price2','item_total_price2')"> - </span>
<!--实际所需菜品的个数-->
<input type="text" value="1" onkeyup="modiffCount(this,'price2','item_total_price2')" />
<span type="plus" onclick="addProdact(this,'price2','item_total_price2')"> + </span>
</div>
<!--该物品的总价: 数量*单价-->
<div class="item_total_price" id="item_total_price2">¥ 65.00</div>
<!--操作-->
<div class="operation">
<!--删除,onclick点击事件-->
<div class="del" onclick="del(this)">删除</div>
</div>
</div>
<!-- 所选菜品条目三 -->
<div class="item">
<!--订单条目的单选复选框-->
<div><input type="checkbox" class="item_checkbox" /></div>
<!--所选菜品的图片-->
<div class="item_img"><img src="./img/foot3.webp" class="img" alt="">
</div>
<!--菜品介绍-->
<div class="promotion">
<!--宣传语-->
<div class="promotion_content">时尚雪纺衫女长袖2020春装新款内搭显瘦衬衫高档洋气小衫上衣打底</div>
<!--三个小图标-->
<div class="three_icons">
<span></span>
<span></span>
<span></span>
</div>
</div>
<!-- sku 菜品的参数信息-->
<div class="sku">
<!--菜品的种类介绍-->
<div class="sku_color">菜品的种类: 热菜</div>
<!--菜品名-->
<div class="sku_size">菜品: xxxL</div>
</div>
<!--菜品的价格-->
<div class="price">
<!--原价 del:横线-->
<div class="price1"><del>¥ 998.00</del></div>
<!--实际活动价格-->
<div class="price2">¥ 666.00</div>
<!--定义隐藏域,用于获取该物品的单价-->
<input type="hidden" id="price3" value="666.00" />
</div>
<!--所选菜的数量-->
<!--所选商品的数量-->
<div class="num">
<span type="minux" onclick="minusProdact(this,'price3','item_total_price3')"> - </span>
<!--实际所需菜品的个数-->
<input type="text" value="1" onkeyup="modiffCount(this,'price3','item_total_price3')" />
<span type="plus" onclick="addProdact(this,'price3','item_total_price3')"> + </span>
</div>
<!--该物品的总价: 数量*单价-->
<div class="item_total_price" id="item_total_price3">¥ 666.00</div>
<!--操作-->
<div class="operation">
<!--删除,onclick点击事件-->
<div class="del" onclick="del(this)">删除</div>
</div>
</div>
</div>
<!--结算-->
<div class="checkout">
<!--全选处理-->
<div class="checkalldiv">
<span class="delchecked" onclick="delAll()">删除选中的菜</span>
</div>
<!--合计-->
<div class="checkout2">
<!--使用span盒子,不会独占行,以块的形式显示-->
已选菜品<span class="checkedcount">0</span> 件
<span class="totaltext">合计: </span>
<span class="total_price">0.00</span>
<span class="checkoutbtn">结算</span>
</div>
</div>
</div>
</body>
</html>
2. 页面元素的外观和位置布局样式:CSS
- 初始化 css样式布局,浏览器的兼容性
- 通过类选择器,额可以一次性写多个,使用逗号隔开,空格后面的就是子元素了。
/* 处理四个店铺中的小图标 */
/* 类选择器可以一次性写多个,使用逗号隔开,空格后面的是子元素 */
.shop_icon,
.shop_icon2,
.three_icons span {
width: 30px;
height: 30px;
background-image: url(./img/log.jpg);
background-size: cover;
/*display显示样式:inline-block:以块的形式展现出来,但不会独占行*/
display: inline-block;
/* auto: 背景图像的真实大小。
cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器。
contain: 将背景图像等比缩放到宽度或高度与容器的宽度或高度相等,背景图像始终被包含在容器内。 */
border-radius: 50%;
}
- 通过伪类选择符
:hover
设置鼠标点击,删除,添加数量,结算时的鼠标经过的小手样式,以及文本的颜色变化。
/* 设置删除,添加数量,结算的鼠标经过的小手样式 */
.num:hover,
.favorite:hover,
.del:hover,
.delchecked:hover,
.checkoutbtn:hover {
color: #f31919;
cursor: pointer;
}
具体的完整代码如下:
/* 初始化界面布局 */
* {
/* 外补丁 上下左右*/
margin: 0;
/* 内补丁 上下左右 */
padding: 0;
/* 标准盒子 */
box-sizing: border-box;
}
h1 {
/* 文本居中 */
text-align: center;
padding-top: 30px;
/* 字体间的间距 */
letter-spacing: 1cm;
/* 字体样式 */
font-family: "隶书";
/* 字体粗细 */
font-size: 50px;
color: #FA7709;
}
/* 处理四个店铺中的小图标 */
/* 类选择器可以一次性写多个,使用逗号隔开,空格后面的是子元素 */
.shop_icon,
.shop_icon2,
.three_icons span {
width: 30px;
height: 30px;
background-image: url(./img/log.jpg);
background-size: cover;
/*display显示样式:inline-block:以块的形式展现出来,但不会独占行*/
display: inline-block;
/* auto: 背景图像的真实大小。
cover: 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器。
contain: 将背景图像等比缩放到宽度或高度与容器的宽度或高度相等,背景图像始终被包含在容器内。 */
border-radius: 50%;
}
/* 设置整体餐单的样式*/
.shops {
/* 宽度 */
width: 1100px;
/* 背景颜色 */
background-color: aliceblue;
/* 设置所有的:div居中的方式: 左右两边的外补丁为 auto自动 */
margin-left: auto;
margin-right: auto;
/* 字号大小 */
font-size: 13px;
color: #121111;
}
/* 设置菜单之间的间距,使用向下的外补丁 */
.shop {
margin-bottom: 20px;
}
/*
设置餐厅名称的位置,
让店铺的名称和左右两边的图标相互对齐
实现方式:对店铺的名称进行一个相对定位,处理与下面的div的间距 */
.shop_name {
/* relative 相对定位 */
position: relative;
/* 相对离下面的div块的距离 */
bottom: 3px;
}
/* 设置餐厅名称头上的4个元素的右补丁控制间距 */
.shop_checkbox,
.shop_icon,
.shop_name,
.shop_icon2 {
margin-right: 10px;
}
/* 设置餐厅的名称与下面的间距,打上外补丁 */
.header {
/* 向下打外补丁 */
margin-bottom: 10px;
}
/*
设置所有(类名一致)餐单条目的样式,因为其中的我们采用的都是
div块,div块是独占行的,所以这里我们进行一个弹性布局 */
.item {
/* 弹性布局,div不会独占行,所以的div块,默认横线排列 */
display: flex;
/* 设置物品条目的背景色 */
background-color: #fafafa;
/* 设置div边框的像素,已经边框线样式,颜色 */
border: 1px solid #dad9d9;
/* 设置各个店铺的条目中的物品的间距,使用向内下打补丁 */
padding-bottom: 20px;
/* 设置各个店铺的条目中的物品的间距,使用向内上打补丁 */
padding-top: 20px;
}
/* 设置条目中菜品图片的大小 */
.img {
width: 100px;
height: 100px;
background-size: contain;
}
/* 设置条目菜品图片与菜品其他参数的间距:右外补丁 */
.item_checkbox,
.item_img {
margin-right: 10px;
}
/* 设置广告宣传部分的div的样式 */
.promotion {
/* 弹性布局,div不会独占行,默认是横向布局 */
display: flex;
/* 将弹性布局默认的flex横向布局,修改为纵向布局 */
flex-direction: column;
/* 将div块的内容两端对齐 */
justify-content: space-between;
width: 250px;
/* 设置右外补丁,控制该广告div右边的间距 */
margin-right: 50px;
}
/* 设置sku菜品参数的样式,这里设置右外补丁,控制间距 */
.sku {
width: 150px;
margin-right: 100px;
}
/* 设置条目中共菜品的价格的样式 */
.price {
width: 80px;
/* 控制间距右外打补丁 */
margin-right: 60px;
/* 字体加粗 */
font-weight: 600;
}
/* 原价的字体样式:字体颜色 */
.price1 {
color: #8a8989d0;
}
/* 所选菜品数量的div样式 */
.num {
width: 60px;
height: 30px;
/*背景颜色*/
background-color: #dad8d8;
/* 用于平衡左右的 +,- 向左边打内补丁 */
padding-left: 8px;
/* 控制间距,使用右外补丁 */
margin-right: 45px;
}
/* 输入菜品数量的文本框的大小以及内容样式 */
.num input {
width: 25px;
height: 25px;
/* 设置文本框中文字居中 */
text-align: center;
}
/* 设置总价的字体样式 */
.item_total_price {
width: 60px;
/* 字体加粗样式 */
font-weight: 600;
color: #f31919;
margin-right: 30px;
}
/* 设置操作div的样式布局 */
.operation {
width: 100px;
height: 60px;
/* 弹性布局,div不会独占行,默认是横向布局 */
display: flex;
/* 将弹性布局默认的flex横向布局,修改为纵向布局 */
flex-direction: column;
/* 将div块的内容两端对齐 */
justify-content: space-between;
/*设置文字的字号大小*/
font-size: 15px;
}
/* 设置结算界面的样式 */
.checkout {
/* 弹性布局,div会不独占行,默认横向排列 */
display: flex;
/* 将弹性布局中的两个大的div 两端对齐 */
justify-content: space-between;
background-color: #dad8d8;
height: 70px;
/* 向上打外补丁,控制间距 */
margin-top: 20px;
/* 向上打内补丁,控制间距 */
padding-top: 20px;
}
/* 删除全选菜品的样式处理 */
.delchecked {
margin-left: 20px;
}
/* 全选设置样式,向右外打补丁控制间距 */
.checkall {
margin-right: 100px;
}
/* 设置合计,结算样式,向左外打补丁,控制间距 */
.totaltext,
.checkoutbtn {
margin-left: 30px;
}
/* 结算左边的样式布局 */
.checkout2 {
margin-top: -20px;
}
/* 设置结算中的件数,以及合计的字体样式 */
.checkedcount,
.totaltext,
.total_price {
/* 字体字号 */
font-size: 20px;
/* 字体加粗 */
font-weight: 600;
color: #f31919;
}
/*设置结算的样式*/
.checkoutbtn {
width: 85px;
height: 70px;
background-color: #555555;
/*设置div块,弹性布局,防止div独占行*/
display: inline-block;
/* 设置字体字号 */
font-size: 30px;
color: white;
/* 字体居中 */
text-align: center;
/* 字体的高 */
line-height: 70px;
}
/* 设置删除,添加数量,结算的鼠标经过的小手样式 */
.num:hover,
.favorite:hover,
.del:hover,
.delchecked:hover,
.checkoutbtn:hover {
color: #f31919;
cursor: pointer;
}
3. 网页模型的定义与页面交互:JavaScript
- 实现对菜品数量的输入的一个校验和修改,以及计算总值
- 用户输入的内容只能是数字
[0~9]
的数字,但是第一个数字不能是 0 - 当用户输入的内容不符合条件时,将值置为 1
- 输入的数值不能,小于 1 大于 64在 [1~64] 的范围内
- 菜品的数据修改了,重新计算商品的总价
定义一个名为 function modiffCount(thisNode, thisPrice, thisItem)
thisNode: 当前需要输入的本文框对象 this (用于获取value值)。
thisPrice: 对应菜品的单价:隐藏域<input type=“hidden”>对应存放的单价的 id 属性
(用于获取value值)。
thisItem: 对应显示总价的位置的 id属性
(用于修改其innerText=显示的内容)
function modiffCount(thisNode, thisPrice, thisItem) {
}
判断用户输入的内容是否符合条件,通过传入的 thisNode 对象获取到 value值,再与编写的正则表达式/^[1-9][0-9]*$/
(第一个数值只能是1~9,第二个数值是 0~9) ,使用 test() 函数与正则表达式,比较判断是否符合条件,
- 符合条件,就再判断输入的数值是不是
>64
的,因为我们通过正则表达式的校验是不会有 小于 0 的结果走到这的,结果大于 64 ,不合理,将用户输入的内容置为 value = 1, 如果小于 64, 则调用sumPrice(thisNode, thisPrice, thisItem)
函数,重新计算菜品的总价:数量 * 单价 - 不符合条件 ,即为输入的内容不合理,将用户输入的内容置为 value = 1。
// 对输入的物品的数量进行一个校验,
// 1.输入的只能是数字[0-9]的数字,但是第一数字不能为0
// 当输入的内容不符合条件是,置为 1
// 2. 输入的数值不能 小于 1 大于 64
// 3. 菜品的数量修改了,重新计算菜品的总价
// 参数分别是 thisNode:当前需要输入的本文框对象(用于获取value值),
//thisPrice:隐藏域<input type="hidden">对应存放的单价的 id 属性(用于获取value值),
//thisItem对应显示总价的位置的id属性(用于修改其innerText=显示的内容)
function modiffCount(thisNode, thisPrice, thisItem) {
// console.log(thisNode.value) 测试
// 获取到文本框中输入的value值
var userInput = thisNode.value;
// console.log(userInput) 测试
// 当输入的数据不符合如下正则表达式,不合法,置为1
// 该正则的意思是,第一个数字可以是[1-9],第二个数字是[0-9],*其中第二个数字可以没有也可以有
var regExp = /^[1-9][0-9]*$/ //
var ok = regExp.test(userInput) // 匹配返回true,否则返回false
if (ok) {
// 符合全是数字
// 再判断是否是在 [1,64]之间,不是value置为 1
// 这里 1就不用判断了,上面的正则校验了
if (thisNode.value > 64) {
// 超过了最大数量 value 置为 1
thisNode.value = 1;
}
// 最后,合理:重新计算总价
sumPrice(thisNode, thisPrice, thisItem) // 注意传的是对象
} else {
// 不符合条件,vaLue 置为 1
thisNode.value = 1;
}
}
- 实现对菜品总价的一个计算(数量 * 单价)
我们定义一个名为 function sumPrice(thisNode, thisPrice, thisItem) {
的函数计算菜品的总价的函数
thisNode: 当前需要输入的本文框对象 this (用于获取value值)。
thisPrice: 对应菜品的单价:隐藏域<input type=“hidden”>对应存放的单价的 id 属性
(用于获取value值)。
thisItem: 对应显示总价的位置的 id属性
(用于修改其innerText=显示的内容)
其中使用了一个方法toFixed()
用于保留几位小数,处理的。
我们通过传的参数的对象,数量 * 单价 计算出菜品的总价,再通过 item_total_price.innerText
修改总价的显示的文本内容。
// 计算单个物品的总单价
// 通过传入,对应商品的单价的id,提高该函数的复用性
// 参数分别是 thisNode:当前需要输入的本文框对象(用于获取value值),
//thisPrice:隐藏域<input type="hidden">对应存放的单价的 id 属性(用于获取value值),
//thisItem对应显示总价的位置的id属性(用于修改其innerText=显示的内容)
function sumPrice(thisNode, thisPrice, thisItem) {
var userNum = thisNode.value
// console.log(userNum) 测试
// 获取到隐藏域中的对应的单价的 value值
var price1 = document.getElementById(thisPrice).value
// console.log(price1) 测试
// 计算出总价
// toFixed() 保留几位小数
var total = (userNum * price1).toFixed(2)
// console.log(total) 测试
// 将总价设置显示到对应的 div当中
var item_total_price = document.getElementById(thisItem)
// console.log(item_total_price1) 测试
item_total_price.innerText = "¥" + total
}
- 商品数量
+
号 的功能实现
实现思路
因为我们的 + 号 和 记录商品总数的文本框在同一个 父节点的div当中 id 为item
,
所以我们可以使用 thisNode.parentNode.childNodes;
获取父节点下的所有子节点的对象集合
DOM parentNode 属性返回指定节点的父节点。
DOM childNodes 属性可返回指定节点的子节点的节点列表(数组)。
DOM tagName 属性返回被选元素的标签名。注意是大写的
再通过循环遍历该子节点的对象集合,通过方法childNodes[i].tagName
找到记录到
商品数量的文本框的标签名(注意是大写的 INPUT
),找到后通过该对象获取到其中的 value(商品的数量)+1,达到按键+ 1 的效果
最后,修改记录商品数量的文本框的value值,
再通过复用sumPrice(childNodes[i], thisPrice, thisItem)
函数重新求总价
注意:需要将从文本框中读取到的字符,转换为数值,才能计算,使用parseInt()
方法。
parseInt()
函数可解析一个字符串,并返回一个整数。
注意:复用上述实现的,商品数量的输入 function sumPrice(thisNode, thisPrice, thisItem)
的函数,防止+加到不合法的数值了,当加到 > 64
的值时,不合理置为 1。
// 商品数量 + 功能的实现
/*
实现思路
因为我们的 + 号 和 记录商品总数的文本框在同一个 父节点的div当中,
所以我们可以使用 thisNode.parentNode.childNodes; 获取父节点下的所有子节点的对象集合
再通过循环遍历该子节点的对象集合,通过方法childNodes[i].tagName 找到记录到
商品数量的文本框的标签名,找到后通过该对象获取到其中的 value(商品的数量)+1,达到按键+ 1 的效果
最后,修改记录商品数量的文本框的value值,
再通过复用sumPrice(thisNode)函数重新求总价
注意:需要将从文本框中读取到的字符,转换为数值,才能计算parseInt() 函数可解析一个字符串,并返回一个整数。
注意:复用上述实现的,商品数量的输入,防止+加到不合法的数值了
*/
//参数分别是 thisNode:当前调用该函数的对象(用于获取value值),
//thisPrice:隐藏域<input type="hidden">对应存放的单价的 id 属性(用于获取value值),
//thisItem对应显示总价的位置的id属性(用于修改其innerText=显示的内容)
function addProdact(thisNode, thisPrice, thisItem) {
// console.log(thisNode) 测试
var newCount; // 数量的统计计算
// 获取到父节点中所有子节点的(对象数组集合),注意是集合
// DOM parentNode 属性返回指定节点的父节点。
// DOM childNodes 属性可返回指定节点的子节点的节点列表(数组)。
var childNodes = thisNode.parentNode.childNodes;
var i // 定义到外面用于标记,
// 循环遍历父节点的所有子节点找记录“商品数量信息的” input标签名,
for (i = 0; i < childNodes.length; i++) {
// console.log(childNodes[i].tagName) 测试获取到对应的标签的名称
//DOM tagName 属性返回被选元素的标签名。注意是大写的
if (childNodes[i].tagName == "INPUT") { // 注意是大写的,找到了退出
// parseInt() 函数可解析一个字符串,并返回一个整数。
// 需要将从文本框中读取到的字符,转换为数值,才能计算
newCount = parseInt(childNodes[i].value) + 1;
// console.log(newCount) 测试
break;
}
}
// 然后修改条目[文本框]中的商品的数量
childNodes[i].value = newCount
// 复用上述实现的,商品数量的输入,防止+到不合法的数值了
modiffCount(childNodes[i], thisPrice, thisItem)
// 调用计算总和的函数,重新计算商品的总价
sumPrice(childNodes[i], thisPrice, thisItem) // 注意传的是对象
}
- 商品数量
-
号 的功能实现
和 + 号是一样的原理是一样的,不同的是,将 + 号,变成了 - 号,不过同样需要复用上述实现的,商品数量的输入 function sumPrice(thisNode, thisPrice, thisItem)
的函数,防止- 减到不合法的数值了,当减到 < 1
的值时,不合理置为 1。
// 同理处理,商品数量 - 功能的实现
//参数分别是 thisNode:当前调用该函数的对象(用于获取value值),
//thisPrice:隐藏域<input type="hidden">对应存放的单价的 id 属性(用于获取value值),
//thisItem对应显示总价的位置的id属性(用于修改其innerText=显示的内容)
function minusProdact(thisNode, thisPrice, thisItem) {
// 获取到该父节点的所有子节点的数组集合
var childNodes = thisNode.parentNode.childNodes;
var newCount; // 计算数量
var i; // 定义在循环外面用于标记
// 循环遍历找到,记录商品数量的标签名称
for (i = 0; i < childNodes.length; i++) {
// console.log(childNodes[i].tagName) 测试获取到对应的标签名
if (childNodes[i].tagName == "INPUT") { // 注意是大写的
// console.log(childNodes[i].tagName) 测试
// 找到了,value -1
// 需要将从文本框中读取到的字符,转换为数值,才能计算
// parseInt() 函数可解析一个字符串,并返回一个整数。
newCount = parseInt(childNodes[i].value) - 1
// console.log(newCount) 测试
break; // 找到了,跳出循环不要再找了
}
}
// 重新修改文本框中的商品数量
childNodes[i].value = newCount
// console.log(childNodes[i].value) 测试
// 复用上述实现的,商品数量的输入,防止 -减到0了,
modiffCount(childNodes[i], thisPrice, thisItem)
// 重新计算总价
sumPrice(childNodes[i], thisPrice, thisItem) // 注意传的参数是对象
}
- 删除对应的条目的菜品
定义一个函数名为function del(thisNode)
, thisNode:当前调用该函数的对象
注意,对应删除这种无法回滚的操作,我们需要让用户进行一个再三的确认,防止用户操作误删了。
这里我们使用 window.confirm()
确认提示框,让用户再次确认是否需要删除数据,点击确定,返回 true,取消返回 false
对于删除,使用函数remove()
,需要注意的是我们删除的是整个菜品的条目的 div ,不是单个其中的一个 div ,所以这里我需要通过,不断的向上翻找,找到对应该 条目菜品的 div ;如下,需要经过两次 parentNode
父节点的翻找。
// 删除对应的条目的菜品
//参数分别是 thisNode:当前调用该函数的对象
function del(thisNode) {
// 1. 删除之前要提示用户是否确认删除,使用window.confirm确认提示框,点击确认,返回true,取消返回false
var ok = window.confirm("您,确定要删除该菜品吗 ?")
if (ok) {
// 1. 确认删除,使用 remove()删除从一个 Dictionary 对象中删除一个主键,条目对。
// 获取到当前节点的父节点的父节点就是这个条目物品的div 这里时先是操作的div,最后时该条目的div
// parentNode 属性返回指定节点的父节点。
thisNode.parentNode.parentNode.remove()
}
}
- 菜品全选和取消全选
定义一个名为 function allCheckbox(thisNode)
函数,实现菜品的全选和取消全选,
thisNode: 表示全选的对象
这里我们需要使用到一个复选框的方法属性 checked
可以获取到其中,复选框是否被选中,选中返回true,没有选中返回false。
首先我们需要通过,复选框的类名document.getElementsByClassName("item_checkbox")
获取到对应的菜品复选框的对象的集合,需要注意的是,这里通过的是类名,因为三个菜品的复选框的类名是一致的,所以获取到的对象是一个集合数组,不是单个对象。
如果我们的全选勾上了,则所有的菜品的复选框就要勾上,如果我们的全选没有勾上,则取消所有的菜品的复选框的勾选。
这里使用遍历循环的方式,将所有的单菜品的对象的 checked 都赋值成 全选的 checked 的值,只有全选勾上了 checked == true, 没有勾上就是 checked = false; checkbox[i].checked = allCheckbox.checked
// 全选和取消全选
function allCheckbox(thisNode) {
var allCheckbox = thisNode
// console.log(allCheckbox) 测试
// 复选框对象中 checked方法可以获取到其中,复选框是否被选中,选中返回true,没有选中返回false
// console.log(allCheckbox.checked) 测试
// 通过类名,获取到同类名的标签对象(注意返回的是多个对象的集合数组)
var checkbox = document.getElementsByClassName("item_checkbox")
for (var i = 0; i < checkbox.length; i++) {
// console.log(checkbox[i]) 测试
// 当全选复选框勾上时,将所有单个复选框勾上
checkbox[i].checked = allCheckbox.checked
}
}
- 删除选中的菜品条目
同样对于删除这种不可回滚的危险操作,需要让用户再次确认,防止用户误操作。
定义一个名为function delAll()
的方法, 用于实现删除选中的菜品条目
删除选中的菜品条目的是和上面删除菜品是一样的原理,找到对应的以及被勾选上的需要删除的菜品条目的 div,
循环遍历删除复选框所选中的菜品,判断是否被选中 checked == true ,为 true 就删除。
注意是从后面开始,因为删除是有顺序的,当你删除了一个,数组长度就变小了,
数组长度变小了,但是对应要删除的节点的标签中的下标没有改变,当长度变成了0
但是你还有一个选中的下标为 1 的节点,没有删除,但是你却遍历不到了,因为数组长度变小了(因为前面删除了对应的下标的节点。从而导致无法删除。所以我们需要从后面开始遍历所获取到的同类名的复选框对象集合数组。
function delAll() {
var allCheckbox = document.getElementsByClassName("item_checkbox")
// 删除前需要提示用户是否确认删除,
var ok = window.confirm("您,确定要删除全部选中的菜品吗")
if (ok) {
// 循环遍历删除复选框所选中的菜品
// 注意是从后面开始,因为删除是有顺序的,当你删除了一个,数组长度就变小了,
// 数组长度变小了,但是对应要删除的节点的标签中的下标没有改变,当长度变成了0
// 但是你还有一个选中的下标为 1 的节点,没有删除,但是你由遍历不到了,因为数组长度变小了(因为前面删除了对应的下标的节点)
for (var i = allCheckbox.length - 1; i >= 0; i--) {
// console.log(allCheckbox.length) 测试
// console.log(allCheckbox[i]) 测试
if (allCheckbox[i].checked) {
// console.log(allCheckbox[i].checked) 测试
// console.log(i) 测试
// 注意删除的是该菜品条目的父节点的 div 标签,
allCheckbox[i].parentNode.parentNode.remove()
}
}
}
}
完整代码如下:
// 对输入的物品的数量进行一个校验,
// 1.输入的只能是数字[0-9]的数字,但是第一数字不能为0,
// 当输入的内容不符合条件是,置为 1
// 2. 输入的数值不能 小于 1 大于 64
// 3. 菜品的数量修改了,重新计算菜品的总价
// 参数分别是 thisNode:当前需要输入的本文框对象(用于获取value值),
//thisPrice:隐藏域<input type="hidden">对应存放的单价的 id 属性(用于获取value值),
//thisItem对应显示总价的位置的id属性(用于修改其innerText=显示的内容)
function modiffCount(thisNode, thisPrice, thisItem) {
// console.log(thisNode.value) 测试
// 获取到文本框中输入的value值
var userInput = thisNode.value;
// console.log(userInput) 测试
// 当输入的数据不符合如下正则表达式,不合法,置为1
// 该正则的意思是,第一个数字可以是[1-9],第二个数字是[0-9],*其中第二个数字可以没有也可以有
var regExp = /^[1-9][0-9]*$/ //
var ok = regExp.test(userInput) // 匹配返回true,否则返回false
if (ok) {
// 符合全是数字
// 再判断是否是在 [1,64]之间,不是value置为 1
// 这里 1就不用判断了,上面的正则校验了
if (thisNode.value > 64) {
// 超过了最大数量 value 置为 1
thisNode.value = 1;
}
// 最后,合理:重新计算总价
sumPrice(thisNode, thisPrice, thisItem) // 注意传的是对象
} else {
// 不符合条件,vaLue 置为 1
thisNode.value = 1;
}
}
// 计算单个物品的总单价
// 通过传入,对应商品的单价的id,提高该函数的复用性
// 参数分别是 thisNode:当前需要输入的本文框对象(用于获取value值),
//thisPrice:隐藏域<input type="hidden">对应存放的单价的 id 属性(用于获取value值),
//thisItem对应显示总价的位置的id属性(用于修改其innerText=显示的内容)
function sumPrice(thisNode, thisPrice, thisItem) {
var userNum = thisNode.value
// console.log(userNum) 测试
// 获取到隐藏域中的对应的单价的 value值
var price1 = document.getElementById(thisPrice).value
// console.log(price1) 测试
// 计算出总价
// toFixed() 保留几位小数
var total = (userNum * price1).toFixed(2)
// console.log(total) 测试
// 将总价设置显示到对应的 div当中
var item_total_price = document.getElementById(thisItem)
// console.log(item_total_price1) 测试
item_total_price.innerText = "¥" + total
}
// 商品数量 + 功能的实现
/*
实现思路
因为我们的 + 号 和 记录商品总数的文本框在同一个 父节点的div当中,
所以我们可以使用 thisNode.parentNode.childNodes; 获取父节点下的所有子节点的对象集合
再通过循环遍历该子节点的对象集合,通过方法childNodes[i].tagName 找到记录到
商品数量的文本框的标签名,找到后通过该对象获取到其中的 value(商品的数量)+1,达到按键+ 1 的效果
最后,修改记录商品数量的文本框的value值,
再通过复用sumPrice(thisNode)函数重新求总价
注意:需要将从文本框中读取到的字符,转换为数值,才能计算parseInt() 函数可解析一个字符串,并返回一个整数。
注意:复用上述实现的,商品数量的输入,防止+加到不合法的数值了
*/
//参数分别是 thisNode:当前调用该函数的对象(用于获取value值),
//thisPrice:隐藏域<input type="hidden">对应存放的单价的 id 属性(用于获取value值),
//thisItem对应显示总价的位置的id属性(用于修改其innerText=显示的内容)
function addProdact(thisNode, thisPrice, thisItem) {
// console.log(thisNode) 测试
var newCount; // 数量的统计计算
// 获取到父节点中所有子节点的(对象数组集合),注意是集合
// DOM parentNode 属性返回指定节点的父节点。
// DOM childNodes 属性可返回指定节点的子节点的节点列表(数组)。
var childNodes = thisNode.parentNode.childNodes;
var i // 定义到外面用于标记,
// 循环遍历父节点的所有子节点找记录“商品数量信息的” input标签名,
for (i = 0; i < childNodes.length; i++) {
// console.log(childNodes[i].tagName) 测试获取到对应的标签的名称
//DOM agName 属性返回被选元素的标签名。注意是大写的
if (childNodes[i].tagName == "INPUT") { // 注意是大写的,找到了退出
// parseInt() 函数可解析一个字符串,并返回一个整数。
// 需要将从文本框中读取到的字符,转换为数值,才能计算
newCount = parseInt(childNodes[i].value) + 1;
// console.log(newCount) 测试
break;
}
}
// 然后修改条目[文本框]中的商品的数量
childNodes[i].value = newCount
// 复用上述实现的,商品数量的输入,防止+到不合法的数值了
modiffCount(childNodes[i], thisPrice, thisItem)
// 调用计算总和的函数,重新计算商品的总价
sumPrice(childNodes[i], thisPrice, thisItem) // 注意传的是对象
}
// 同理处理,商品数量 - 功能的实现
//参数分别是 thisNode:当前调用该函数的对象(用于获取value值),
//thisPrice:隐藏域<input type="hidden">对应存放的单价的 id 属性(用于获取value值),
//thisItem对应显示总价的位置的id属性(用于修改其innerText=显示的内容)
function minusProdact(thisNode, thisPrice, thisItem) {
// 获取到该父节点的所有子节点的数组集合
var childNodes = thisNode.parentNode.childNodes;
var newCount; // 计算数量
var i; // 定义在循环外面用于标记
// 循环遍历找到,记录商品数量的标签名称
for (i = 0; i < childNodes.length; i++) {
// console.log(childNodes[i].tagName) 测试获取到对应的标签名
if (childNodes[i].tagName == "INPUT") { // 注意是大写的
// console.log(childNodes[i].tagName) 测试
// 找到了,value -1
// 需要将从文本框中读取到的字符,转换为数值,才能计算
// parseInt() 函数可解析一个字符串,并返回一个整数。
newCount = parseInt(childNodes[i].value) - 1
// console.log(newCount) 测试
break; // 找到了,跳出循环不要再找了
}
}
// 重新修改文本框中的商品数量
childNodes[i].value = newCount
// console.log(childNodes[i].value) 测试
// 复用上述实现的,商品数量的输入,防止 -减到0了,
modiffCount(childNodes[i], thisPrice, thisItem)
// 重新计算总价
sumPrice(childNodes[i], thisPrice, thisItem) // 注意传的参数是对象
}
// 删除对应的条目的菜品
//参数分别是 thisNode:当前调用该函数的对象
function del(thisNode) {
// 1. 删除之前要提示用户是否确认删除,使用window.confirm确认提示框,点击确认,返回true,取消返回false
var ok = window.confirm("您,确定要删除该菜品吗 ?")
if (ok) {
// 1. 确认删除,使用 remove()删除从一个 Dictionary 对象中删除一个主键,条目对。
// 获取到当前节点的父节点的父节点就是这个条目物品的div 这里时先是操作的div,最后时该条目的div
// parentNode 属性返回指定节点的父节点。
thisNode.parentNode.parentNode.remove()
}
}
// 全选和取消全选
function allCheckbox(thisNode) {
var allCheckbox = thisNode
// console.log(allCheckbox) 测试
// 复选框对象中 checked方法可以获取到其中,复选框是否被选中,选中返回true,没有选中返回false
// console.log(allCheckbox.checked) 测试
// 通过类名,获取到同类名的标签对象(注意返回的是多个对象的集合数组)
var checkbox = document.getElementsByClassName("item_checkbox")
for (var i = 0; i < checkbox.length; i++) {
// console.log(checkbox[i]) 测试
// 当全选复选框勾上时,将所有单个复选框勾上
checkbox[i].checked = allCheckbox.checked
}
}
// 删除所有被选中的菜品
function delAll() {
var allCheckbox = document.getElementsByClassName("item_checkbox")
// 删除前需要提示用户是否确认删除,
var ok = window.confirm("您,确定要删除全部选中的菜品吗")
if (ok) {
// 循环遍历删除复选框所选中的菜品
// 注意是从后面开始,因为删除是有顺序的,当你删除了一个,数组长度就变小了,
// 数组长度变小了,但是对应要删除的节点的标签中的下标没有改变,当长度变成了0
// 但是你还有一个选中的下标为 1 的节点,没有删除,但是你由遍历不到了,因为数组长度变小了(因为前面删除了对应的下标的节点)
for (var i = allCheckbox.length - 1; i >= 0; i--) {
// console.log(allCheckbox.length) 测试
// console.log(allCheckbox[i]) 测试
if (allCheckbox[i].checked) {
// console.log(allCheckbox[i].checked) 测试
// console.log(i) 测试
// 注意删除的是该菜品条目的父节点的 div 标签,
allCheckbox[i].parentNode.parentNode.remove()
}
}
}
}
/*
function oneCheckbox(thisNode) {
var allCheckbox = document.getElementById("allCheckbox")
var childNodes = thisNode.parentNode.childNodes
for (var i = 0; i < childNodes.length; i++) {
// console.log(childNodes[i].tagName) 测试找到对应 Input 标签名
childNodes[i].onclick = function () {
// alert(111) 测试
// 循环遍历所有的复选框的状态,当总数和选中的总数量相等的时候,第一个全选复选框选中checked=true,其他情况下第一个全选框取消选中 checked=false
var count = 0;
for (var i = 0; i < childNodes.length; i++) {
// 选中count++计数
if (childNodes[i].checked) {
count++;
}
}
// window.alert(aihao.length) 测试u
// console.log(count) 测试
if (count == childNodes.length) {
// 等于复选框的总数,勾上全选
allCheckbox.checked = true;
} else { // 总数不相等,取消第一个全选的勾选
allCheckbox.checked = false;
}
}
}
}
*/
想要源代码及其相关图片素材的,大家可以移动到 如下的 Github,Gitee 中去,我已经将其托管到如下仓库了。自行获取。
- Project/期末设计 · 彩虹海/HTML_CSS_JS仓库 - 码云 - 开源中国 (gitee.com)
- project_warehouse/期末设计 at master · China-Rainbow-sea/project_warehouse (github.com)
7. 总结
- 写项目之前,先拟定好程序架构图,由整体再到局部,
架构拟定好了,分清了主干
。这样无论你怎么编写代码都不会太偏离主干。写的代码才不会乱,犯浑。干起来也不会太累。最后在分好层级,根据拟定好的架构图,紧跟着主干,一点一点的添加细节。当你把所有的细节都编写好后,项目也就基本上写好了。 - CSS 的初始化,用于浏览器的兼容性问题。
- 在 html 中调用函数时,传的参数含有 字符串时,注意单双引号交替使用
- 背景颜色的一个渐变色的使用
background: linear-gradient(200deg, #517fa4, #243949);
border-radius: 20px;
可以处理边框的锐度,让边框不是一个正方的,而是圆润的,50%是一个圆box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
设置边框的凹凸的阴影效果background: rgba(186, 221, 253, 0.7);
表单的背景颜色 rgba(red,green,blue,最后一个是背景的透明度(值越小越透明)) ,还可以使用:opacity: xxx
属性参数的不透明度是以数字表示的,从0.0到1.0,完全透明是0.0,完全不透明是1.0,数字越大代表元素越不透明。outline: none;
去除我们点击 input 文本框时,显示的一个边框- 让表单中的 input 文本框中的边框只显示 下面的边框线的 css处理样式
/*先让 input 文本框中的所有边框都不显示出来*/
border: none; /* none去除边框设置 *
/*再单独设置 input 文本框下面的边框显示出来,以及显示的格式*/
border-bottom: 1px solid rgba(24, 12, 12, 0.651); /* 设置用户名和密码下边框的样式 */
outline: none;
去除我们点击 input 文本框时,显示的一个边框- 使用绝对定位处理,外加一些 外打补丁,让表单整体居中
.container {
/* 相对定位,让表单居中显示 */
position: absolute;
top: 50%;
left: 50%;
margin-left: -200px;
margin-top: -250px;
}
- 当所需背景图片因为过大,加载过慢时,可以使用
background-image: url(),url()
两个url 图片的加载、 - 对应 javascript 代码的编写,F12 打开浏览器的调试窗口,通过
console.log()
将参数信息打印显示到 浏览器的控制台上,或者使用window.alert()
在弹窗上显示参数信息 - 使用一个
window.onload
的方式定义一个统一的匿名函数,该函数的存放所有的对象的绑定的校验的事件的函数。只有当页面所有的数据加载完了,才会触发该事件,调用其中的所有对应事件绑定的匿名函数 ,改变 js 程序的执行顺序,去除掉 id 没有加载到页面中的影响。 - 通过对象绑定注册匿名函数,不同的层面处理不同的功能的,一个清晰的划分。
- 当css 位置不合理时,可以使用相对定位,或绝对定位处理,想要控制间距时,可以使用对内,对外打补丁。
8. 最后:
限于自身水平,其中存在的错误,希望大家多多指教,韩信点兵——多多益善,谢谢大家。后会有期,江湖再见 !!!