HTML、CSS 和 JavaScript 的小技巧
极客的东西
本文最初发表于medium.com
一份很棒的 HTML、CSS 和 JavaScript 入门指南列表,包含日常使用的基本概念。欢迎留言分享你自己的方法 :)
使用 CSS 禁用所有内容
CSS
.disabled {
filter: grayscale(1);
pointer-events: none;
}
在此处查看 JSFiddle 。
将数组拆分成不可变的块
JS
const array = [1, 2, 3, 4]
const size = 3
const new_array = array.reduce((acc, a, i) => {
i % size ? acc[parseInt(i / size)].push(a) : acc.push([a])
return acc
}, [])
甚至更短:
const new_array = array.reduce((acc, a, i) =>
i % size ? acc : [...acc, array.slice(i, i + size)], [])
请记住,开始使用const
,如果您需要更改其值,则使用let
并尽可能避免var
。
在此处查看 JSFiddle 。
保存和加载日期
始终将日期时间保存在 UTC ISO 中,并使用本地 ISO 将其加载到用户界面。使用原生小部件可避免日期格式偏好设置(例如中端、小端等)。
HTML
<input type="datetime-local">
<button>Save</button>
<button>Load</button>
JS
$button_save.onclick = () =>
localStorage.setItem('datetime', $input.value &&
new Date($input.value).toISOString())
$button_load.onclick = () =>
$input.value = localStorage.getItem('datetime') &&
toLocalISOString(new Date(localStorage.getItem('datetime')))
.slice(0, -8)
function toLocalISOString(d) {
const offset = d.getTimezoneOffset()
return new Date(
d.getFullYear(),
d.getMonth(),
d.getDate(),
d.getHours(),
d.getMinutes() - offset,
d.getSeconds(),
d.getMilliseconds()).toISOString()
}
在此处查看 JSFiddle 。
我建议使用sessionStorage
和localStorage
。如果不是绝对必要,请勿滥用 Cookie。如果您需要更多本地存储空间,可以使用 IndexedDB。
通过单击标题选择 HTML 表格列
JS
document.querySelectorAll('th').forEach($th =>
$th.onclick = event => {
document.querySelectorAll(`td:nth-of-type(${event.currentTarget
.cellIndex + 1})`)
.forEach($td => $td.classList.toggle('selected'))
})
请记住,onclick
始终覆盖前一个函数(如果有的话),用于addEventListener()
多个函数。
在此处查看 JSFiddle 。
解构时重命名
我们将在对对象数组进行排序时重命名时间属性
。JS
let times = [
{name:'dog', time: '10:23'},
{name: 'laundry', time: '09:34'},
{name: 'work', time: '11:00'}]
times.sort(({ time: a }, { time: b }) => a < b ? -1 : a > b ? 1 : 0)
记住,sort()
改变原始数组。
在此处查看 JSFiddle 。
自动完成下拉菜单
你用过 jQuery UI 或 Bootstrap 第三方选项的自动完成下拉菜单吗?简直一团糟。
幸运的是,几年前我们就得到了一个期待已久的解决方案:原生 HTML5 自动完成下拉菜单datalist
。这是一个支持所有设备的轻量级标准。
HTML
<input list="series">
<datalist id="series">
<option value="Adventure Time">
<option value="Rick and Morty">
<option value="Game of Thrones">
<option value="Planet Earth 2">
</datalist>
在此处查看 JSFiddle 。
节省您的工具时间和依赖性,尽可能少地使用库和框架!
使用 CSS Grid 实现真正轻松的响应
CSS Grid 是处理响应能力最简单、最干净、最强大的方法,这是近年来发展起来的一种全新方法,可以随时使用。
CSS Grid 改变了您以前布局文档的方式,不再需要使用 divitis(大量的 div)和 JavaScriptdiv
根据屏幕改变位置(Bootstrap 现在所做的),您可以使用纯 CSS 网格布局,只使用有意义的 div,并且不受文档源顺序的影响。
您不需要接触 HTML 或 JavaScript,不需要 Bootstrap 甚至复杂的 CSS 规则,您在 CSS 中看到的内容就是您在屏幕上得到的内容。
HTML
<div class="grid">
<div class="name">Name</div>
<div class="score">Score</div>
<div class="skills">Skills</div>
<div class="chart">Chart</div>
</div>
CSS
.grid {
display: grid;
grid-template-areas:
"name"
"score"
"skills"
"chart";
}
@media only screen and (min-width: 500px) {
.grid {
grid-template-areas:
"name skills"
"score skills"
"chart chart";
}
}
.name {
grid-area: name;
}
.score {
grid-area: score;
}
.skills {
grid-area: skills;
}
.chart {
grid-area: chart;
}
在此处查看 JSFiddle 。
我建议你做这些例子。
像我一样爱上网格模板❤
移动用户界面的各个部分而不影响交互
HTML
<ul>
<li>
<button id="up">Up</button>
<button id="down">Down</button>
</li>
<li>Nothing</li>
<li>Nothing</li>
</ul>
JS
document.querySelector('#up').onclick = e => {
const $li = e.target.parentElement
if ($li.previousElementSibling)
$li.parentElement.insertBefore($li, $li.previousElementSibling)
}
document.querySelector('#down').onclick = e => {
const $li = e.target.parentElement
if ($li.nextElementSibling)
$li.parentElement.insertBefore($li.nextElementSibling, $li)
}
请记住,target
是什么触发了事件,并且currentTarget
是什么分配了监听器。
在此处查看 JSFiddle 。
HTML 输入 24 小时格式的时间
依赖原生 HTML 小部件,无需依赖第三方库。然而,有时会有一些限制。如果您曾经处理过 HTMLinput
时间,并且了解它的含义,请尝试设置最大或最小小时/分钟,以及/或者将 12 小时制更改为 24 小时制,反之亦然。目前为止,避免麻烦的一个好方法是使用两个数字类型的输入框和少量 JS。
HTML
<div>
<input type="number" min="0" max="23" placeholder="23">:
<input type="number" min="0" max="59" placeholder="00">
</div>
CSS
div {
background-color: white;
display: inline-flex;
border: 1px solid #ccc;
color: #555;
}
input {
border: none;
color: #555;
text-align: center;
width: 60px;
}
JS
document.querySelectorAll('input[type=number]')
.forEach(e => e.oninput = () => {
if (e.value.length >= 2) e.value = e.value.slice(0, 2)
if (e.value.length == 1) e.value = '0' + e.value
if (!e.value) e.value = '00'
})
请记住,==
双重比较来判断是否相等,===
三重比较来判断是否相等和类型。
如果要检查变量是否存在undefined
,只需使用三重比较即可a === undefined
,对于 也一样null
。如果要检查它是否存在,请使用typeof a != 'undefined'
。
在此处查看 JSFiddle 。
不使用可变变量循环 n 次
JS
[...Array(10).keys()]
.reduce((sum, e) => sum + `<li>${e}</li>`, '')
也喜欢这个:
[...Array(10)]
.reduce((sum, _, i) => sum + `<li>${i}</li>`, '')
在此处查看 JSFiddle 。
水平和垂直居中
忘记任何复杂的方法,只需使用 Flexbox 并在容器中设置水平中心和垂直中心。
CSS
body {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
div {
background-color: #555;
width: 100px;
height: 100px;
}
在此处查看 JSFiddle 。
异步获取
fetch()
与异步函数一起使用。
JS
async function async_fetch(url) {
let response = await fetch(url)
return await response.json()
}
async_fetch('https://httpbin.org/ip')
.then(data => console.log(data))
.catch(error => console.log('Fetch error: ' + error))
在此处查看 JSFiddle 。
请注意,正如您所注意到的,我没有写;
分号,这完全没问题,在 JavaScript 中这;
不是强制性的,无论您是否写它都没关系,JS 引擎都会检查它并在需要时插入它,只要小心以括号开头的新行(
并避免return
在不同的行中使用值即可。
带有左右按钮的页脚
HTML
<footer>
<div>
<button>Button A</button>
<button>Button B</Button>
</div>
<div>
<button>Button C</button>
<button>Button D</button>
</div>
</footer>
CSS
footer {
display: flex;
justify-content: space-between;
position: fixed;
bottom: 0;
left: 0;
right: 0;
}
在此处查看 JSFiddle 。
滚动到视图中
我创建了 n 个颜色随机的盒子(div),以便随机选择其中一个并使其在视口中可见。每次重新运行代码时,无论其位置如何,您都会在屏幕上看到所选的盒子。
JS
document.querySelector(`div:nth-child(${random})`).scrollIntoView()
在此处查看 JSFiddle 。
展平对象数组
JS
array = alphas.map(a =>
a.gammas.map(g => g.betas)
).join()
如果您想了解使用forEach
withconcat
和 with 的其他不同方法,push
请检查此链接(我也使用 jsPerf 做了一些耗时的测试)。
在此处查看 JSFiddle 。
请记住,如果您想要平坦数组,则可以使用 轻松完成flat()
。
[1, 2, [3, 4, [5, 6]]].flat(Infinity)
嵌套对象数组
返回一个由n个元素填充内容的数组:
JS
let get_array = (n, content) => Array(n).fill(content)
返回具有 name 属性且具有内容值的对象:
let get_object = (name, content) =>
Object.defineProperty({}, name, {value: content})
3 层带有对象的数组(嵌套)
a =
get_array(3, get_object('b',
get_array(6, get_object('c',
get_array(3, {})
))
))
在此处查看 JSFiddle 。
没有重复值的数组
JS
const array = [1, 2, 3, 3, 3, 2, 1]
设置策略:
[...new Set(array)]
过滤策略(比较容易理解,但速度较慢):
array.filter((elem, index) => index == array.indexOf(elem))
在此处查看 JSFiddle 。
记住,Array.from(iterableObj) = [...iterableObj]
带单位的 HTML 输入
HTML
<span><input type="number" min="0" value="50">€</span>
CSS
span {
background: white;
border: 1px solid #e8e8e8;
}
input {
background: inherit;
outline: none;
border: none;
padding: 0 5px;
}
在此处查看 JSFiddle 。
响应式背景循环视频
HTML
<video autoplay loop poster="https://website/video.jpg">
<source src="http://website/video.webm">
</video>
CSS
video.landscape {
width: 100vw;
height: auto;
}
video {
width: auto;
height: 100vh;
}
请记住,您可以添加任意数量的源以支持不同的视频格式。
在此处查看 JSFiddle 。
如何打印特定的 HTML 元素
我想要这样的东西:
document.querySelector('div').print() // maybe in the future
在本文发布时,该标准仅支持window.print()
,但我们可以使用 CSS 和少量 JavaScript 来实现:
CSS
@media print {
body.print-element *:not(.print) {
display: none;
}
}
JS
function print_this(elem) {
document.body.classList.add('print-element')
elem.classList.add('print')
window.print()
document.body.classList.remove('print-element')
elem.classList.remove('print')
}
在此处查看 JSFiddle 。
查看、隐藏、输入和生成密码
喜欢让事情尽可能简单 xD
里面有一个提示input
,然后是button
显示密码,最后是另一个button
生成随机密码。
HTML
<input id="password" type="password" placeholder="type password...">
<button id="view-password"></button>
<button id="generate-password">↻</button>
查看或隐藏密码:
JS
$view_password.addEventListener('click', e => {
e.currentTarget.classList.toggle('view')
if (e.currentTarget.className.includes('view'))
$password.setAttribute('type', 'text')
else $password.setAttribute('type', 'password')
})
设置随机密码并确保显示:
$generate_password.addEventListener('click', () => {
$view_password.classList.add('view')
$password.setAttribute('type', 'text')
$password.value = Math.random().toString(36).slice(-8)
})
在此处查看 JSFiddle 。
请注意,我个人将选择器的命名const
以 $ 开头。
无限选择上一个和下一个
在选择循环中选择每个元素。如果在完成元素列表后立即向前移动,则将从头开始选择;如果反向移动,则同样如此。
HTML
<button id="previous">Previous</button>
<button id="next">Next</button>
<ul>
<li></li>
<li class="selected"></li>
<li></li>
<li></li>
<li></li>
</ul>
JS
document.querySelector('#next').addEventListener('click', () => {
const $selected = document.querySelector('.selected')
const $next_element = $selected.nextElementSibling
if (!$next_element)
$next_element = $selected.parentElement.firstElementChild
$selected.classList.remove('selected')
$next_element.classList.add('selected')
})
记住,使用nextElementSibling
and previousElementSibling
(DOM 元素)而不是nextSibling
and previousSibling
(DOM 对象)。DOM 对象可以是任何东西:注释、独立文本、换行符等等。在我们的例子中,nextSibling
如果我们将所有 HTML 元素放在一起,中间不加任何字符,那么效果会很好:
<ul><li></li><li></li></ul>
在此处查看 JSFiddle 。
响应式正方形
我见过很多创建响应式正方形的奇葩方法,所以想分享一个简单的方法。点击下方的 JSFiddle 链接,尝试调整结果窗口的大小。
CSS
div {
width: 60vw;
height: 60vw;
margin: 20vh auto;
background-color: #774C60;
}
在此处查看 JSFiddle 。
通过鼠标单击定义的圆形区域
我们将根据点击框区域内的位置来定义圆的面积。我们可以使用 JavaScript 事件、一些基础数学知识和 CSS 来处理这个问题。
宽度和高度是相同的,我们为数学设置哪一个并不重要:
JS
const width = e.currentTarget.clientWidth
鼠标光标距离圆心的绝对位置:
const x = Math.abs(e.clientX — offset.left — width / 2)
const y = Math.abs(e.clientY — offset.top — width / 2)
最大值将告诉我们圆面积的百分比:
percent = Math.round(2 * Math.max(x, y) * 100 / width)
$circle.style.width = percent + '%'
$circle.style.height = percent + '%'
文本覆盖
好吧,也许你想着直接在键盘上启用 Insert 键就行了,但如果没有这个键,或者你想在某些特定的输入框和文本区域输入时始终保持(独立的)覆盖模式,该怎么办呢?其实很简单。
JS
$input.addEventListener('keypress', function(e) {
const cursor_pos = e.currentTarget.selectionStart
if (!e.charCode) return
$input.value = $input.value.slice(0, cursor_pos) +
$input.value.slice(cursor_pos + 1)
e.currentTarget.selectionStart =
e.currentTarget.selectionEnd =
cursor_pos
})
在此处查看 JSFiddle 。
使用闭包重置计数器
设置一个带有闭包和一些外部可访问选项的基本计数器。
JS
const add = (function() {
let offset = 0
return function(option) {
switch (option) {
case 0: offset = 0; break;
case 1: offset++; break;
case 2: offset — ; break;
default: throw ‘Not a valid option’;
}
console.log(offset)
}
})()
请记住,闭包只是让你记录并保护你的变量。
在此处查看 JSFiddle 。
无限滚动
你见过向下滚动时自动“加载更多”的功能吗?你在 Tumblr 的图片、Gmail 的消息或 Facebook 上看到过吗?很酷,不是吗?无限滚动是分页的替代方案,而且无处不在。它优化了用户体验,根据用户需求(间接地)加载数据。页面、网页和应用的加载速度更快,它只会加载你需要的内容,而不是全部加载。你无需添加额外的交互、按钮或小部件,因为它本身就带有你习惯的正常阅读行为:用鼠标向下滚动,或者用手指在可触摸屏上滚动。
JS
const $ol = document.querySelector('ol')
function load_more() {
let html = ''
for (var i = 0; i < 5; i++) html += '<li></li>'
$ol.innerHTML += html
}
$ol.addEventListener('scroll', function() {
if ($ol.scrollHeight — $ol.scrollTop == $ol.clientHeight)
load_more()
})
在此处查看 JSFiddle 。
请注意,在上面的例子中,我们可以更有效地创建节点和使用appendChild()
。
材料图标
HTML
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<i class="material-icons">face</i>
在此处查看 JSFiddle 。
使用 box-shadow 的基本 CSS 过渡
当鼠标悬停在元素上时,CSS 会发生变化,并带有缓入缓出过渡效果(缓慢开始和结束)。我们用内阴影(插图)填充元素。
CSS
i {
transition: all 0.5s ease-in-out;
box-shadow: 0 0 0 75px #E94F37 inset;
}
i:hover {
box-shadow: 0 0 0 4px #E94F37 inset;
color:#E94F37;
}
在此处查看 JSFiddle 。
将 HTML 表格导出为 CSV 文件
假设您有一个 HTML 表,并且想要将其下载为 CSV 表。
HTML
<table>
<tr><th>Name</th><th>Age</th><th>Country</th></tr>
<tr><td>Geronimo</td><td>26</td><td>France</td></tr>
<tr><td>Natalia</td><td>19</td><td>Spain</td></tr>
<tr><td>Silvia</td><td>32</td><td>Russia</td></tr>
</table>
首先,您需要从 HTML 转换为 CSV:
JS
let csv = []
let rows = document.querySelectorAll('table tr')
for (var i = 0; i < rows.length; i++) {
let row = [], cols = rows[i].querySelectorAll('td, th')
for (var j = 0; j < cols.length; j++)
row.push(cols[j].innerText)
csv.push(row.join(','))
}
download_csv(csv.join('\n'), filename)
之后,您可以使用Blob
以下链接下载它:
let csvFile = new Blob([csv], {type: 'text/csv'})
let downloadLink = document.createElement('a')
downloadLink.download = filename
downloadLink.href = window.URL.createObjectURL(csvFile)
downloadLink.style.display = 'none'
document.body.appendChild(downloadLink)
downloadLink.click()
在此处查看 JSFiddle 。
键盘事件
用于event.code
以人性化的方式了解哪些键被按下。event.key
如果您想区分大小写,并避免使用浏览器快捷键,例如:Ctrl + P(打印),请使用此选项。
JS
document.onkeydown = event => {
switch (event.code) {
case 'ArrowDown':
$div.style.top = `${parseInt($div.style.top || 0) + step}px`
break
case 'KeyR':
if (event.altKey) $div.style.top = 0
break
}
}
在此处查看 JSFiddle 。
类似 jQuery 的短选择器
当你必须选择 DOM 元素时,使用 JavaScript 会有点烦人,在这种情况下,我们可能会错过 jQuery,因为原生 JavaScript 实在太长了
。JS
// Select one element (first one)
document.querySelector('#peter')
document.querySelector('.staff')
document.querySelector('.staff').querySelector('.age')
// Select all elements
document.querySelectorAll('.staff')
我们不喜欢在编码时重复事情,如果您在 JavaScript 的开头定义下一个代码,您将能够做到比 jQuery 更好。
function $(selector) {
return document.querySelector(selector)
}
function $$(selector) {
return document.querySelectorAll(selector)
}
Element.prototype.$ = function(selector) {
return this.querySelector(selector)
}
Element.prototype.$$ = function(selector) {
return this.querySelectorAll(selector)
}
现在你可以把我们的例子写得更短一些:
// Select one element
$('#peter')
$('.staff')
$('.staff').$('.age')
// Select all elements
$$('.staff')
这很容易记住,因为 $ 的行为类似于带有 CSS 选择器的 jQuery,而 $$ 的作用也类似,但它允许你选择多个元素。第一个 $$ 返回找到的第一个元素,第二个 $$ 返回元素列表。
还有一件事,您不能在此代码中使用 jQuery,因为 jQuery 也使用 $,如果您需要它,您必须将代码中的 $ 更改为另一个东西,即:qS。
记住,在 JavaScript 中我们有比类更好的东西:prototype
。不管你使用没有class
,底层使用的是prototype
。
属性 (property) 和特性 (attribute) 有何不同?
属性 (property) 位于 DOM 中;属性 (attribute) 位于解析到 DOM 中的 HTML 中。
HTML
<body onload="foo()">
JS
document.body.onload = foo
当不需要逻辑时,避免使用 switch 语句
数组更快,在下一个例子中,如果您想知道现在是第九个月,您只需编写代码即可months[9]
。
JS
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
极客的东西
★ 你和我一样热爱 HTML、CSS 和 JavaScript 吗?^^ 别忘了看看我关于 Web 开发的极客穿搭;P
文章来源:https://dev.to/gengns/short-tricks-of-html-css-and-javascript-582g