JavaScript 小工具
正反选
使用Js
完成正反选的效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
justify-content: center;
align-items: center;
display: flex;
width: 100vw;
height: 100vh;
}
main {
justify-content: center;
align-items: center;
display: flex;
flex-flow: column;
border: 1px solid #ddd;
padding: 10px;
}
</style>
</head>
<body>
<main>
<section>
<button data-select="all">全选</button>
<button data-select="cancel">取消</button>
<button data-select="versa">反选</button>
</section>
<p></p>
<table border="1px" style="border-collapse: collapse;">
<caption style="border: 1px solid #1F1F1F;border-bottom:none;">爱好</caption>
<tbody>
<tr>
<td><input type="checkbox" name="hobby"></td>
<td>篮球</td>
</tr>
<tr>
<td><input type="checkbox" name="hobby"></td>
<td>足球</td>
</tr>
<tr>
<td><input type="checkbox" name="hobby"></td>
<td>排球</td>
</tr>
</tbody>
</table>
</main>
</body>
<script>
"use strict";
window.onload = () => {
bind();
}
function bind() {
document.querySelectorAll("button").forEach((element) => {
element.addEventListener("click", select)
});
}
function select() {
let choice = event.target.dataset.select;
let inputList = document.getElementsByName("hobby");
inputList.forEach((element) => {
if (choice == "all") { // 全选
element.checked = true;
} else if (choice == "cancel") { // 取消
element.checked = false;
} else { // 反选
element.checked = !element.checked;
}
});
}
</script>
</html>
二级联动
使用Js
完成二级联动
当
<selcet>
标签中某一个<option>
被选中时,可以对该<select>
标签使用selectedIndex
属性拿到被选中的<option>
索引值,并且<section>
标签还有一个options
属性用于拿到其下的所有<options>
标签。使用
change
事件,文本框在内容发生改变并失去焦点时触发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="">
<select name="province">
<option value="未选择">请选择省份</option>
</select>
<select name="city">
<option value="未选择">请选择城市</option>
</select>
</form>
</body>
<script>
window.onload = () => {
init();
}
function init() {
let data = { 浙江省: ["杭州", "宁波", "嘉兴"], 四川省: ["成都", "绵阳", "德阳"], 江苏省: ["南京", "苏州", "无锡"] }; // 模拟后台数据
let province = document.querySelector("[name='province']");
let city = document.querySelector("[name='city']");
for (let name of Object.keys(data)) {
let option = document.createElement("option");
option.innerText = name;
province.append(option);
}
province.addEventListener("change", function () { // 不用箭头函数,箭头函数指向window或undefined
city.length = 1; // 每次更新内容,只留下 <option value="未选择">请选择城市</option>
let provinceName = this.options[this.selectedIndex].innerText; // options所有的option标签,selectedIndex当前选中的option索引
let cityData = data[provinceName];
cityData.forEach((cityName) => {
let option = document.createElement("option");
option.innerText = cityName;
city.append(option);
});
})
}
</script>
</html>
模态对话框
在有的场景中,当点击登录或者注册的按钮后整个页面会弹出登录和注册的一个窗口。其他部分均变为灰色,我们可以利用calss
的增删改查来实现这一需求和功能。
模态对话框一般分为三层
第一层显示层,显示其他主要的信息,如页面主题等
第二层遮罩层,当点击某一特定按钮后触发
第三层功能层,登录或注册功能的功能均在此层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
width: 100vw;
height: 100vh;
position: relative;
}
main {
width: 100%;
height: 100%;
background: deeppink;
}
main button {
position: absolute;
right: 2%;
top: 2%;
}
aside {
width: 100%;
height: 100%;
background: darkslategray;
opacity: .3;
position: absolute;
top: 0;
z-index: 2;
}
article {
padding: 10px;
background-color: deepskyblue;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 3;
}
article form * {
margin: 5px;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<main>
<!-- 显示层 -->
<button>登录</button>
</main>
<aside class="hidden">
<!-- 遮罩层 -->
</aside>
<article class="hidden">
<!-- 功能层 -->
<form action="#">
<h2>欢迎登录</h2>
<p><input type="text" name="username" placeholder="用户名"></p>
<p><input type="text" name="username" placeholder="密码"></p>
<button type="submit">提交</button>
<button type="button">取消</button>
</form>
</article>
</body>
<script>
window.onload = () => {
init();
}
function init() {
let showBTN = document.querySelector("main button"); // 登录按钮
let hiddenBTN = document.querySelector("article.hidden form button[type='button']"); // 取消按钮
let cover = document.querySelector("aside.hidden");
let login = document.querySelector("article.hidden");
showBTN.addEventListener("click", () => {
cover.classList.toggle("hidden"); // 若有hidden则取消,没有则添加
login.classList.toggle("hidden");
});
hiddenBTN.addEventListener("click", () => {
cover.classList.toggle("hidden");
login.classList.toggle("hidden");
});
}
</script>
</html>
左侧菜单栏
左侧三个菜单栏,点击任何一个隐藏另外两个,其实这个小功能用jQuery
来做简单的很。使用Js
稍稍有点小复杂不过其实还好。
用
Map
来做一个容器,让其与对应的菜单标签建立联系。当点击一个标题时循环容器,将非事件目标的对应标签设置为隐藏,将事件目标的对应标签设置为显示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
list-style: none;
}
body {
width: 100vw;
height: 100vh;
display: flex;
}
section {
width: 30%;
height: 100%;
display: flex;
flex-flow: column;
justify-content: start;
align-items: center;
background-color: deeppink;
cursor: pointer;
}
section article {
width: 95%;
margin: 10px 0;
}
section article h1 {
display: flex;
justify-content: center;
background-color: #fff;
box-shadow: 10px 10px 5px rgba(100, 100, 100, .5);
}
section article ul li {
display: flex;
justify-content: center;
color: #fff;
margin: 5px 0;
}
main {
width: 70%;
height: 100%;
background-color: deepskyblue;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 50px;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<section>
<article>
<h1>HTML学习</h1>
<ul class="hidden">
<li>HTML基础</li>
<li>HTML进阶</li>
<li>HTML高级</li>
</ul>
</article>
<article>
<h1>CSS学习</h1>
<ul class="hidden">
<li>CSS基础</li>
<li>CSS进阶</li>
<li>CSS高级</li>
</ul>
</article>
<article>
<h1>JavaScript学习</h1>
<ul class="hidden">
<li>Js基础</li>
<li>Js进阶</li>
<li>Js高级</li>
</ul>
</article>
</section>
<main>
</main>
</body>
<script>
let h1List = document.querySelectorAll("section article h1");
let relationship = new Map();
let liList = document.querySelectorAll("section article ul li")
let main = document.querySelector("main");
h1List.forEach((ele) => {
let menu = ele.nextElementSibling; // 即ul标签
relationship.set(ele, menu);
ele.addEventListener("click", function () {
for (let [key, value] of relationship) {
if (key == this) {
value.classList.toggle("hidden");
} else {
value.classList.add("hidden");
}
}
}
);
})
liList.forEach((ele) => {
ele.addEventListener("click", function () {
main.innerText = "您选择了:" + this.innerText;
});
ele.addEventListener("mouseenter", function () {
this.style.color = "blue";
});
ele.addEventListener("mouseleave", function () {
this.style.color = "white";
});
});
</script>
</html>
轮播图
轮播图为常用的工具,这里用原生Js
进行实现,并将其作为插件的形式,方便今后使用。
轮播图切换并未加入过渡动画
传入图片路径自动生成所有组件与样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
</style>
</head>
<body>
</body>
<script>
window.onload = () => {
let imgPathList = ["./imgs/1.jpg", "./imgs/2.webp", "./imgs/3.webp"]; // 图片路径
new Carousel(imgPathList); // 传入图片路径自动生成
}
class Carousel {
"use strict";
constructor(imgPathList, imgUrlList = [], appendEle = document.body, width = 800, seconds = 1500) {
if (!imgPathList) {
throw new Error("错误!必须传入图片路径");
}
if (imgPathList.length > 12) {
throw new Error("错误!不建议轮播图图片太多");
}
this.imgPathList = imgPathList; // 图片路径
this.imgUrlList = imgUrlList; // 用于跳转的页面路径,应当与图片路径的长度相同
this.appendEle = appendEle; // 轮播图添加进的标签
this.seconds = seconds; // 切换秒数
this.width = width; // 轮播图宽度
this.style = null;
this.ElementMap = new Map();
this.imgList = [];
this.liList = [];
this.prev = null;
this.next = null;
this.index = 0;
this.initStyle(); // 用于生成样式
this.initElement(); // 用于生成标签
this.autoPlay(); // 用于自动播放
this.navClick(); // 用于导航点击
this.buttonBind(); // 用于左侧绑定
}
initElement() {
let section = document.createElement("section");
section.classList.add("show_img");
let ul = document.createElement("ul");
for (let index = 0; index < this.imgPathList.length; index++) {
let a = document.createElement("a");
a.href = this.imgUrlList[index] || "#";
a.style.display = "inline-block";
if (a.href.indexOf("#")) {
a.target = "_self";
} else {
console.log("执行")
a.target = "_blank";
}
let img = document.createElement("img");
img.src = this.imgPathList[index];
img.classList.add("hidden");
img.width = this.width;
this.imgList.push(img);
a.append(img);
let li = document.createElement("li");
this.ElementMap.set(li, img);
this.liList.push(li);
setTimeout(() => { // 异步添加,不会造成页面先渲染后加载的问题
section.append(a);
ul.append(li);
});
}
section.append(ul);
this.prev = document.createElement("article");
this.prev.innerHTML = "<i class='iconfont icon-prev01'>";
this.prev.classList.add("prev");
this.next = document.createElement("article");
this.next.classList.add("next");
this.next.innerHTML = "<i class='iconfont icon-next01'>";
section.append(this.prev);
section.append(this.next);
this.appendEle.append(section);
}
initStyle() {
this.style = document.createElement("style");
this.style.type = "text/css";
let link = document.createElement("link"); // 外链资源
link.rel = "stylesheet";
link.href = "//at.alicdn.com/t/font_1953712_kcy11tbqhi.css";
document.head.append(link);
document.head.append(this.style);
this.style.append(
document.createTextNode(
`
section.show_img * {
margin: 0;
padding: 0;
}
section.show_img {
overflow:hidden;
position: relative;
display: inline-block;
justify-content: center;
align-items: center;
}
section.show_img ul {
display: flex;
justify-content: center;
align-items: center;
list-style: none;
position: absolute;
left: 50%;
top: 90%;
transform: translate(-50%, -50%);
cursor: pointer;
}
section.show_img ul li {
background-color: #fff;
margin: 0 ${this.width * 0.01}px;
width: ${this.width * 0.04}px;
height: ${this.width * 0.01}px;
border-radius: 40px;
transition: ${this.seconds * 0.3}ms;
}
section.show_img article {
width:${this.width * 0.03}px;
height: ${this.width * 0.05}px;
background-color: #eee;
opacity: .3;
position: absolute;
top: 50%;
translate: (-50%, -50%);
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: 300ms;
}
section.show_img article:hover {
opacity: .6;
}
section.show_img article:first-of-type {
left: 0;
border-top-right-radius: 40%;
border-bottom-right-radius: 40%;
}
section.show_img article:last-of-type {
right: 0;
border-top-left-radius: 40%;
border-bottom-left-radius: 40%;
}
section.show_img img {
width: 800px;
transition: 1s;
}
.hidden {
display: none ;
}
.current {
background-color: red !important;
}
`
)
)
}
autoPlay() {
// 先让第一页显示
let currentLi = this.liList[this.index];
let currentImg = this.ElementMap.get(currentLi);
this.show(currentLi, currentImg);
this.index++;
setInterval(() => {
this.hidden(currentLi, currentImg);
currentLi = this.liList[this.index];
currentImg = this.ElementMap.get(currentLi);
if (this.index < this.ElementMap.size - 1) {
this.index++;
} else {
this.index = 0;
}
this.show(currentLi, currentImg);
}, this.seconds)
}
navClick() {
for (let [value, key] of this.ElementMap) {
value.addEventListener("click", (event) => {
let showLi = event.target; // 准备显示的
let showImg = this.ElementMap.get(showLi); // 准备实现的
if (!showLi.classList.contains("current")) { // 是否正在显示
let currentLi = document.querySelector(".current"); // 正在显示的
let currentImg = this.ElementMap.get(currentLi);
this.hidden(currentLi, currentImg);
this.show(showLi, showImg);
this.index = Array.prototype.indexOf.call(this.liList, showLi); // 改变索引值
}
})
}
}
buttonBind() {
this.prev.addEventListener("click", (element) => {
this.nextOrPrev("prev");
});
this.next.addEventListener("click", (element) => {
this.nextOrPrev("next");
});
}
nextOrPrev(position) {
let showLi = null; // 应该显示的
let showImg = null;
let currentLi = document.querySelector(".current"); // 正在显示的
let currentImg = this.ElementMap.get(currentLi);
this.hidden(currentLi, currentImg);
if (position == "prev") {
showLi = currentLi.previousElementSibling;
if (showLi == null) {
showLi = this.liList[this.liList.length - 1];
}
} else {
showLi = currentLi.nextElementSibling;
if (showLi == null) {
showLi = this.liList[0];
}
}
showImg = this.ElementMap.get(showLi);
this.show(showLi, showImg);
this.index = Array.prototype.indexOf.call(this.liList, showLi); // 改变索引值
}
show(li, img) {
li.classList.add("current");
img.classList.remove("hidden");
}
hidden(li, img) {
li.classList.remove("current");
img.classList.add("hidden");
}
}
</script>
</html>
头像预览
效果展示
我们想在注册页面时实现头像预览的功能,该怎么做?
这里需要借助一个FileReader
的文件阅读器,这是Js
内置的一个对象。
实现思路
首先我们需要从后端拿一个默认头像,并且将input:file
进行隐藏。
将默认头像放在label
标签中。注意:label
的for
应该与隐藏的input:file
的id
相同。
<div class="form-group">
<label for="myfile">
<img src="{% static 'img/default.png' %}" width="100px" alt=" style="transform: translateX(10px);" id="myimg">
</label>
<input type="file" name="avatar" id="myfile" style="display: none;">
</div>
然后我们点击默认头像时候,就可以打开资源管理器选择我们自己的头像。
下面就需要借助Js
了。
document.querySelector("#myfile").addEventListener("change", function () {
let fileObj = this.files[0]; // 获取input中的文件
let myFileReaderObj = new FileReader(); // 创建一个文件阅读器
myFileReaderObj.readAsDataURL(fileObj); // 异步操作 文件阅读器读取出上传的图片
myFileReaderObj.addEventListener("load", function () { // 文件阅读器可读时,修改img标签的src位上传的图片
let imgObj = document.querySelector("#myimg"); // 获取img标签,准备替换内容
imgObj.setAttribute("src", myFileReaderObj.result); // 特征属性,用setAttribute进行操作
})
})