
3年Web前端开发工程师,目前就职于上海知名媒体——新闻晨报,个人公众号:前端微站
我们知道微信小程序的转发时的封面图片比例固定为5:4,具体内容详见小程序开发文档: 字段 说明 默认值 最低版本 title 转发标题 当前小程序名称 path 转发路径 当前页面 path ,必须是以 / 开头的完整路径 imageUrl 自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径。支持PNG及JPG。显示图片长宽比是 5:4。 使用默认截图 1.5.0 但是,在实际开发时我们有时需要将某张动态请求的网络图片作为转发封面图(比如文章封面),而这张图片又往往不符合 5:4 的比例要求。这时,如果我们直接将这张图片作为封面图其实也是可以的,只不过将长宽比大于 5:4 作为转发封面时,封面下可能会留有部分空白。 我们可以直接以简书为例,简书目前分享文章到微信时是以小程序的方式分享,而其分享封面也正是取自该文章的封面,所以,如果我们从小程序中转发可能会看到类似下图的转发封面图: 可以看到,封面图下面存在大块空白,如果图片长宽比例更大,那么下面的空白也将更大。 因此,为了更加美观,我们必须将图片按照 5:4 比例进行适当地裁剪,而我写本文的目的也正是如此。 1. 创建Canvas画布 前端要裁剪图片,我们首先就要想到用Canvas,写H5如此,微信小程序当然也是如此。 <canvas style="position: absolute; top: -1000px; left: -1000px; width: 640px; height: 640px; background: #000" canvas-id="canvas"></canvas> 我们要用到的canvas当然不能直接在页面中显示,所以可以使用负定位值的方式将其隐藏。 2. 下载网络图片 我们可以使用wx.downloadFile()来下载网络图片,也可以使用wx.getImageInfo(),因为我们这里需要获取到图片的宽高,所以这里就要用到wx.getImageInfo()来进行图片的下载。 wx.getImageInfo({ src: "", // 这里填写网络图片路径 success: (res) => { // 这个是我封装的裁剪图片方法(下面将会说到) clipImage(res.path, res.width, res.height, (img) => { console.log(img); // img为最终裁剪后生成的图片路径,我们可以用来做为转发封面图 }); } }); 3. 裁剪图片并导出 以下是我封装的专门用于裁剪图片比例大于 5:4 的图片,裁剪方式是截取图片中间部分(当然你也可以试着写下裁剪小于 5:4 的图片): /* 裁剪封面, src为本地图片路径或临时文件路径, imgW为原图宽度, imgH为原图高度, cb为裁剪成功后的回调函数 */ const clipImage = (src, imgW, imgH, cb) => { // ‘canvas’为前面创建的canvas标签的canvas-id属性值 let ctx = wx.createCanvasContext('canvas'); let canvasW = 640, canvasH = imgH; if (imgW / imgH > 5 / 4) { // 长宽比大于5:4 canvasW = imgH * 5 / 4; } // 将图片绘制到画布 ctx.drawImage(src, (imgW - canvasW) / 2, 0, canvasW, canvasH, 0, 0, canvasW, canvasH) // draw()必须要用到,并且需要在绘制成功后导出图片 ctx.draw(false, () => { setTimeout(() => { // 导出图片 wx.canvasToTempFilePath({ width: canvasW, height: canvasH, destWidth: canvasW, destHeight: canvasH, canvasId: 'canvas', fileType: 'jpg', success: (res) => { // res.tempFilePath为导出的图片路径 typeof cb == 'function' && cb(res.tempFilePath); } }) }, 1000); }) } 本文重点总结 ① 使用Canvas画布进行图片裁剪 ② 裁剪网络图片前,必须使用wx.getImageInfo()下载图片并同时获取图片的宽高
1. 基本思路 ① 遍历Tab选项 ② 然后给每个Tab选项绑定点击事件 ③ 每次点击时清除所有Tab选项及Tab选项内容的样式,然后给当前Tab选项添加标记样式,给当前Tab选项添加显示样式 2. 具体代码实现 <!--HTML--> <div id="tab"> <!--Tab选项--> <div class="tab-nav"> <a href="javascript:;" class="cur">选项1</a> <a href="javascript:;">选项2</a> <a href="javascript:;">选项3</a> </div> <!--Tab选项内容--> <div class="tab-content"> <div class="content content1 cur">选项内容1</div> <div class="content content2">选项内容2</div> <div class="content content3">选项内容3</div> </div> </div> /*CSS*/ #tab{ width: 300px; margin: 30px auto;} .tab-nav{ display: flex;} .tab-nav a{ flex: 1; line-height: 40px; border: 1px solid #000; text-align: center; text-decoration: none; color: #000;} .tab-nav a.cur{ color: #fff; background: #000;} .content{ display: none; width: 100%; height: 300px; color: #fff; box-sizing: border-box; padding: 10px;} .content.cur{ display: block;} .content1{ background: #f00; } .content2{ background: #0f0;} .content3{ background: #00f;} //Javascript window.onload = function () { var oTab = document.getElementById('tab'); var aTabNav = oTab.querySelectorAll('.tab-nav a'); var aTabContent = oTab.querySelectorAll('.tab-content .content'); for(var i = 0; i < aTabNav.length; i++){ // 遍历Tab选项 aTabNav[i].onclick = (function (i) { // Tab选项绑定点击事件 return function () { // 清除所有Tab选项的标记样式 for(var j = 0; j < aTabNav.length; j++){ aTabNav[j].classList.remove('cur'); } aTabNav[i].classList.add('cur'); // 清除所有Tab选项内容的显示样式 for(j = 0; j < aTabContent.length; j++){ aTabContent[j].classList.remove('cur'); } aTabContent[i].classList.add('cur'); } })(i); } }; 以上JS还可以继续优化,将相同功能的代码封装成函数: window.onload = function () { var oTab = document.getElementById('tab'); var aTabNav = oTab.querySelectorAll('.tab-nav a'); var aTabContent = oTab.querySelectorAll('.tab-content .content'); for(var i = 0; i < aTabNav.length; i++){ aTabNav[i].onclick = (function (i) { return function () { addCurClass(aTabNav,i); addCurClass(aTabContent,i); } })(i); } }; function addCurClass(obj,index) { for(var i = 0; i < obj.length; i++){ obj[i].classList.remove('cur'); } obj[index].classList.add('cur'); } 代码没有什么难度,非常基础,但是需要注意的一点是,在获取当前点击项的索引时,我们需要使用到闭包。 相关推荐 教你两招用纯CSS写Tab切换纯CSS制作单页Web应用使用hash制作单页Web应用
一、需求分析 1. 数组最大3项 看到这个,首先就应该想到排序,我们可以先将数组从大到小进行排序,然后再获取排序后的数组前3项即可。 所需函数: 排序: sort()获取前3项: filter() 2. 获取索引值 当我们将数组进行排序后,数组的索引值会发生变化,我们便无法再获取原始数组的索引值了,所以我们得在排序前对数组进行处理。 具体处理思路是,通过遍历将原始数组中的每一项与其索引捆绑在一起,我们可以使用对象数组的形式来实现捆绑,这样即使数组被重新排序,我们最终也能够找到原始数组每项的索引值。 所需函数: 通过遍历创建对象数组: map() 3. 整体实现思路 ① 重构:通过遍历创建对象数组,其中每一项包含索引值和数值 —— map() ② 排序:根据对象数组每一项中的数值从大到小进行排序 —— sort() ③ 筛选:通过筛选获取排序后的前3项组成数组 —— filter() ④ 提取:通过遍历获取前3项数组的原始索引值 —— map() 二、具体代码实现 function fetchMaxIndex(arr){ return arr.map(function (item,i) { // 重构,将数组每项绑定相应索引 return { key: i, value: item } }) .sort(function (a,b) { // 排序,根据每项数值排序 return b.value - a.value; }) .filter(function (item,i) { // 筛选,获取排序后前三项 return i < 3 }) .map(function (item) { // 提取,获取前三项原始索引 return item.key }); } console.log(fetchMaxIndex([4,6,9,3,0,8,1])); // [2, 5, 1] 若对以上map()和filter()不了解,请戳→简述forEach()、map()、every()、some()和filter()的用法; 若想了解以上sort()排序函数的具体用法,请戳→JS数组排序 若想了解更多数组处理函数,请戳→JS数组操作之增删改查; 本文重点总结: 对于数组的处理,无非就那么几个常用的函数方法,经常使用便能熟能生巧
JS数组排序方法有两个:reverse()和sort(),其中reverse()可将数组进行倒序,而sort()则可将数组项灵活地进行升序或降序排列。 一、reverse() var arr = [8,4,9,1]; console.log(arr.reverse()); // [1, 9, 4, 8] console.log(arr); // [1, 9, 4, 8] 可以看出,reverse()会直接改变原数组,并且返回值也是倒序后的数组。 二、sort() 记得当年学C语言时,要学各种各样的排序算法,比如经典的冒泡排序法、二分排序法等,现在抛开这些算法不说,JS就自带原生的排序函数,用起来非常方便,它就是sort()。 1. 不传参数 var arr = [8,4,9,1]; console.log(arr.sort()); // [1, 4, 8, 9] console.log(arr); // [1, 4, 8, 9] 可以看出,sort()不传参数时会按升序方式对数组项进行排序,并且与reverse()一样既改变原数组,同时返回的也是排序后的数组。 我们再来看下一个例子: var arr = [8,90,9,16]; console.log(arr.sort()); // [16, 8, 9, 90] 这时你可能会说,不对呀,最终排序返回的不应该是[8, 9, 16, 90]吗?然鹅事实返回的却是[16, 8, 9, 90],这到底是哪门子逻辑? 事实上,sort()并不是按照数值进行排序,而是按字符串字母的ASCII码值进行比较排序的,所以当数组项为数字时,sort()也会自动先将数字转换成字符串,然后再按字母比较的规则进行排序处理。 现在我们再回头看看前面两个例子。当arr为[8,4,9,1]时,数组每一项转换成字符串后进行排序的结果正好与数字排序结果相同;而当arr为[8,90,9,16]时,数组每一项转换成字符串后就得按顺序一位一位进行比较,比如升序排序时,“16”应该排在最前面,因为“16”的第一位是“1”,比“8”和“9”的ASCII码值都要小。 啰嗦了这么多,其实我们实际很少会使用这种排序方式,而更多的应该就是纯数字的排序。那么我们该如何正确地使用sort()来达到预期的排序效果呢? 接下来就来看看传参后的sort()能给我们怎样的精彩表现。 2. 传入一个函数 这个函数参数功能其实很简单,实际上就是告诉sort()排序方式到底是升序还是降序,我们还是来看具体实例吧~ var arr = [8,90,9,16]; // 升序 console.log(arr.sort(function (a, b) { return a - b; })); // [8, 9, 16, 90] // 降序 console.log(arr.sort(function (a, b) { return b - a; })); // [90, 16, 9, 8] 这种用法的规则是,当sort()传入函数中的第一个参数a位于第二个参数b之前,则返回一个负数,相等则返回0,a位于b之后则返回正数。 比如,当要做升序排序时,我们需要想到前面的数肯定是要比后面的数小,所以传入的这个函数参数返回值应该要是个负数,因此函数参数返回a - b。 如果实在不好理解,我们可以干脆记下来,a - b升序,b - a降序,但是需要注意的是,如果按照这种记忆方式的话,函数括号内的两个参数a和b的书写顺序可不能颠倒哦~ 本文重点总结: ① sort() 不传参时默认为升序,且是按字符串比较的方式排序;传参时,其参数为函数,且该函数带俩参数 a 和 b,返回值 a - b 为升序,b - a为降序 ② reverse() 和 sort() 两函数均会改变原数组,且返回值同样也是改变后的数组
CSS中的background属性想必大家已经用了无数遍,但是对于CSS3属性background-clip你可能还不太了解,那么今天我们就专门来聊聊这个属性。 clip,英文意为 “裁切,修剪”,所以很显然,background-clip属性肯定与背景裁切有关,而事实也正是如此。 background-clip存在以下四个属性值,他们分别是: border-box、padding-box、content-box 和 text 接下来我将通过具体实例来对background-clip这几个属性值一一进行讲解。 1. 不设置 background-clip 属性 /*CSS*/ .box{ display: inline-block; width: 200px; height: 200px; margin: 20px; padding: 20px; border: 10px dashed #000; background-color: #ff0;} <!--HTML--> <div class="box">想要学习更多前端知识,欢迎关注微信公众号:前端微站</div> 显示效果: 图一 2. border-box /*CSS*/ .box1{ background-clip: border-box;} <!--HTML--> <div class="box box1">想要学习更多前端知识,欢迎关注微信公众号:前端微站</div> border,意味着将边框以外的背景部分裁掉,所以最终效果与图一相同,也就是说,border-box是background-clip的默认属性值。 3. padding-box /*CSS*/ .box2{ background-clip: padding-box;} <!--HTML--> <div class="box box2">想要学习更多前端知识,欢迎关注微信公众号:前端微站</div> padding,意味着将内边距以外的背景部分裁掉,所以最终效果如图二所示: 图二 4. content-box /*CSS*/ .box3{ background-clip: content-box;} <!--HTML--> <div class="box box3">想要学习更多前端知识,欢迎关注微信公众号:前端微站</div> content,意味着将内容以外的背景部分裁掉,所以最终效果如图三所示: 图三 5. text /*CSS*/ .box4{ -webkit-text-fill-color: transparent; background: -webkit-linear-gradient(right,#0f0,#00f); -webkit-background-clip: text;} <!--HTML--> <div class="box box4">想要学习更多前端知识,欢迎关注微信公众号:前端微站</div> text,意味着将文字轮廓以外的背景部分裁掉而只留下文字轮廓以内的部分,而此时我们只需要通过设置文字为透明色即可透过文字看到盒子背景色,所以最终效果如图四所示: 图四 兼容性: background-clip: text ,目前需要加上webkit前缀 本文重点总结 ① background-clip 用于背景裁切,有 border-box、padding-box、content-box 和 text 四个属性值 ② border-box 裁掉边框以外的背景部分,为默认值 ③ padding-box 裁掉内边距以外的背景部分 ④ content-box 裁掉内容以外的背景部分 ⑤ text 裁掉文字轮廓以外的背景部分,目前需加webkit前缀
我在之前文章Webpack轻松入门(二)——CSS打包中提到过Webpack中的自动打包功能,很简单,在webpack.config.js中添加 watch: true 配置,打包一次之后每次代码更新后都会自动进行打包而无需重复输入命令行。 当然,我们也可以直接给package.json中的scripts添加相关配置,而无需更改webpack.config.js。 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack --watch" } 换句话说,package.json中的webpack --watch与webpack.config.js中的watch: true效果相同。 实际上,Webpack还提供了一个比watch更方便的功能,它不仅可以实现自动打包,还具有自动打开浏览器和自动刷新页面的功能,可以说给我们这帮懒人服务到了极致,哈哈。 下面我们就来看看具体如何实现这样的功能。 1. 安装webpack-dev-server npm i -D webpack-dev-server 2. package.json中的scripts添加相关配置 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack", "dev": "webpack-dev-server --open" } 其中 --open 就代表打开默认浏览器。 3. 输入命令行进行打包 npm run dev 打包结束后你会发现浏览器自动打开,并且正确显示打包后的页面。 4. 更改任意代码 我们可以试着在index.js中添加以下代码: var module = require('./module.js'); alert(module.text); module.js: var text = 'Hello Webpack!'; module.exports = { text: text }; 保存后你会发现浏览器自动刷新并弹出弹框“Hello Webpack!”。 值得注意的是,通过webpack-dev-server打包后的代码并不会进入dist目录,而是直接创建一个开发服务器,并运行打包后的代码。因此,通常我们会将Webpack打包分为两种模式:开发模式和生产模式。 开发模式顾名思义就是给我们开发时候用的,这时候我们就可以用上webpack-dev-server,从代码自动打包到自动开启浏览器再到自动刷新全部自动化,提高了工作效率;生产模式顾名思义就是最终代码上线时候用的,这时候我们就只需使用其最基础的打包功能,最终打包后的代码会进入dist目录,我们只需要将其上传服务器即可。 本文重点总结 ① 使用 webpack-dev-server 可自动创建开发服务器,实现代码从自动打包到自动刷新页面的自动化开发模式 ② Webpack有两种打包模式:开发模式和生产模式,开发模式下可使用 webpack-dev-server 提高开发效率
这次我们玩点有意思的,先看看效果→趣味表白效果 最好是在PC端预览哦,下面出简单的小白教程。(注意:此教程仅适合前端小白,高手请绕过) 1. 在电脑桌面点击鼠标右键,选择【新建】→【文本文档】 2. 双击打开已在桌面上新建好的文本文档,复制粘贴以下代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>你喜欢我吗?</title> <style> *{ margin: 0; padding: 0;} a{ text-decoration: underline;} body{ font-size: 16px; color: #333; -webkit-user-select: none; user-select: none;} .alert{ position: fixed; top: 0; bottom: 0; left: 0; right: 0; background-color: rgba(83, 26, 255, 0.8);} .alert .box{ position: fixed; top: 0; bottom: 0; left: 0; right: 0; width: 400px; height: 240px; margin: auto; padding: 20px; text-align: center; border: 1px solid #eee; border-radius: 10px; box-shadow: 0 0 20px #111; background: #fff; overflow: hidden; -webkit-animation: model .5s; animation: model .5s; -webkit-transition: all .3s; transition: all .3s;} .alert .box::after{ content: '×'; position: absolute; top: 0; right: 0; width: 30px; font-size: 20px; line-height: 30px; cursor: pointer;} .alert .box:hover::after{ display: none;} .alert .title{ font-size: 28px; line-height: 6;} .alert.no-btn .title{ line-height: 8.5;} .alert.no-btn .btns{ display: none;} .alert .btns.reverse{ direction: rtl;} .alert .btn{ display: inline-block; width: 100px; margin: 0 5px; border-radius: 4px; border: 1px solid #ccc; box-sizing: border-box; font-size: 16px; line-height: 2.5; color: #333; background-color: #fff; cursor: pointer;} .alert .btn.like{ color: #fff; background-color: #ff4528; -webkit-transition: all .3s; transition: all .3s;} .alert .btn.like:hover{ background-color: #f93618;} @keyframes model { 0%{ opacity: 0; transform: scale(.5);} 60%{ opacity: 1; transform: scale(1.2);} 100%{ opacity: 1; transform: scale(1);} } @-webkit-keyframes model { 0%{ opacity: 0; -webkit-transform: scale(.5);} 60%{ opacity: 1; -webkit-transform: scale(1.2);} 100%{ opacity: 1; -webkit-transform: scale(1);} } </style> </head> <body> <div id="container"></div> <script> window.onload = function () { model('你喜欢我吗?',true,'喜欢'); hoverBtn(); $('.like').onclick = function () { model('我也喜欢你,我们在一起吧',true,'同意'); hoverBtn(); }; }; function $(selector) { return document.querySelector(selector); } function hoverBtn() { $('.dislike').addEventListener('mouseover',function () { var btns = $('.btns'); if(btns.classList.contains('reverse')){ btns.classList.remove('reverse'); } else{ btns.classList.add('reverse'); } }); } function model(text,hasBtn,btnVal) { var html = '<div class="alert">\n' + ' <div class="box">\n' + ' <h2 class="title">'+ text + '</h2>\n' + ' <div class="btns">\n' + ' <div class="like btn" href="javascript:;">'+ btnVal + '</div>\n' + ' <div class="dislike btn" href="javascript:;">不'+ btnVal +'</div>\n' + ' </div>\n' + ' </div>\n' + ' </div>'; document.getElementById('container').innerHTML = html; if(!hasBtn){ $('.alert').classList.add('no-btn'); } } </script> </body> </html> 3. 选择【文件】→【另存为】 4. 在弹窗底部“文件名”处将“txt”改成“html”,“编码”处选择“utf-8”,点击保存 5. 双击打开桌面上已经另存好的网页文件就可以了 喜欢吗?喜欢就点个赞关注我呗~
到目前为止,有关Webpack最基础的内容差不多已经讲完了,其中包括Webpack运行的基本流程、CSS的打包和图片的打包,也就是说,当你掌握这几节之后,基本上就能像以前不用Webpack时一样愉快地写代码了。 当然,我们其实还有很多需要优化的地方,就比如本文所要讲到的,如何将HTML进行打包? 在之前的实例中,Webpack虽然能够正常地将各种页面所需要的资源从src目录打包至dist目录,但是我们在最后必须在dist目录中手动去创建HTML页面,并引入这些打包后的资源。 事实上,Webpack能够自动的帮助我们完成这件事,只需要使用html-webpack-plugin插件即可。 1. 安装html-webpack-plugin插件 与安装loader一样,命令行输入以下命令回车即可。 npm i -D html-webpack-plugin 2. webpack.config.js中添加相关配置 const htmlWebpackPlugin = require('html-webpack-plugin'); // 引入html-webpack-plugin插件 module.exports = { entry: './src/scripts/index.js', // 打包入口 output: { path: __dirname + '/dist', // 输出路径 filename: 'scripts/index.js' // 输出文件名 }, module: { rules: [ // 其中包含各种loader的使用规则 { test: /\.css$/, // 正则表达式,表示打包.css后缀的文件 use: ['style-loader','css-loader'] // 针对css文件使用的loader,注意有先后顺序,数组项越靠后越先执行 }, { // 图片打包 test: /\.(png|jpg|gif|svg)$/, loader: 'url-loader', options: { name: './images/[name].[ext]', limit: 8192 } } ] }, plugins: [ // 打包需要的各种插件 new htmlWebpackPlugin({ // 打包HTML template: './src/index.html' // HTML模板路径 }) ], watch: true // 监听文件改动并自动打包 }; 3. 添加HTML模板文件 HTML模板文件如下: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Hello Webpack</title> </head> <body> </body> </html> 是的,除了HTML本身,其他资源如CSS、图片等均无需手动添加,在打包结束后,所有资源均会自动添加至HTML文件相应位置并进入dist目录。 4. 打包结束 打包后的index.html文件如下: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Hello Webpack</title> </head> <body> <script type="text/javascript" src="scripts/index.js"></script></body> </html> 本文重点总结 打包HTML,使用 html-webpack-plugin 插件即可
我们知道,在Webpack中,js文件类型是能够被识别并直接打包的,而其他文件类型(如CSS和图片等)则需要通过特定的loader来进行加载打包。 上一节我们讲到如何使用css-loader和style-loader两个loader来打包CSS代码,这次我将继续讲解如何使用loader将图片类型文件进行打包处理。 一、上节回顾 为了让大家思路更加明朗,我还是先将上一节结束时的目录结构和一些关键文件内容展示一下吧。 目录结构 webpack.config.js: module.exports = { entry: './src/scripts/index.js', // 打包入口 output: { path: __dirname + '/dist', // 输出路径 filename: 'scripts/index.js' // 输出文件名 }, module: { rules: [ // 其中包含各种loader的使用规则 { test: /\.css$/, // 正则表达式,表示打包.css后缀的文件 use: ['style-loader','css-loader'] // 针对css文件使用的loader,注意有先后顺序,数组项越靠后越先执行 } ] }, watch: true // 监听文件改动并自动打包 }; index.js: var module = require('./module.js'); require('../css/style.css'); 二、图片打包 图片打包关键要用到file-loader或url-loader,其中url-loader与file-loader功能基本一致,只不过url-loader能将小于某个大小的图片进行base64格式的转化处理。 1. CSS中的图片 比如,我现在在src目录下新增一个images文件夹,再在images中添加一张图片“1.jpg”。然后我在style.css中添加以下代码: html{ height: 100%; background: url("../images/1.jpg") no-repeat center;} 如果这时直接进行打包,那么肯定会报错,比如像这样的: 第三行它提示你需要使用一个loader来处理图片这种类型的文件,这时,我们只需把file-loader装上,并在webpack.config.js中添加相应配置就ok了。 ① 输入命令安装file-loader npm i -D file-loader ② 在webpack.config.js中的rules数组中添加file-loader的相关配置 { test: /\.(png|jpg|gif|svg)$/, use: ['file-loader'] } 其实对于只有单个loader的,我们还可以将其简化成下面这样: { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader' } 接下来,我们只需执行npm start命令进行打包即可。 虽然我们已经将图片但是打包后,我们会发现打包后的图片名发生了变化: 那么如何才能保持图片名不变,而且也能够添加到指定目录下呢? 我们只需要再添加一个options属性即可: { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: 'images/[name].[ext]' } } 其中name属性其实就是图片打包后的路径,其中包括图片名([name])和图片格式([ext])。 此时打包后的dist目录结构如下: 2. JS中的图片 file-loader能自动识别CSS代码中的图片路径并将其打包至指定目录,但是JS就不同了,我们来看下面的例子。 // index.js var img = new Image(); img.src = '../images/1.jpg'; document.body.appendChild(img); 如果不使用Webpack打包,正常情况下只要路径正确图片是能够正常显示的。然而,当使用Webpack打包后,我们会发现图片并未被成功打包到dist目录,自然图片也无法显示出来。 这其实是因为Webpack并不知道'../images/1.jpg'是一张图片,如果要正常打包的话需要先将图片资源加载进来,然后再将其作为图片路径添加至图片对象。具体代码如下: // index.js var imgSrc = require('../images/1.jpg'); var img = new Image(); img.src = imgSrc; document.body.appendChild(img); 3. 浅谈url-loader 除了使用file-loader对图片进行打包处理外,我们同样也可以使用url-loader代替,另外我们还可以对小于某个大小的图片进行base64格式的转化处理。 { test: /\.(png|jpg|gif|svg)$/, loader: 'url-loader', options: { name: './images/[name].[ext]', limit: 8192 } } 这里limit属性的作用就是将小于8192B(8.192K)大小的图片转成base64格式,而大于这个大小的图片将会以file-loader的方式进行打包处理,例如: 当然,如果不写limit属性,那么url-loader就会默认将所有图片转成base64。 小贴士: 当我们配置watch为true进行打包后,Webpack会一直处于监听状态,然而当更改webpack.config.js后我们仍需要重新进行打包操作,这时我们只需在控制台简单的按下Ctrl+C后根据提示输入字母y回车确定即可成功退出监听状态。 本文重点总结 ① 使用file-loader或url-loader可对图片进行打包操作 ② url-loader可将图片转成base64格式
说到Tab切换,你可能首先想到的就是使用jQuery,短短几行代码就可以轻松搞定一个Tab切换。 而今天所要分享的,是使用 0 行JS代码来实现Tab切换! 具体效果如下: Tab切换 方法一:模拟单选框原理 关于模拟单选框,在我之前文章中有讲到,详情请戳→纯CSS模拟单选框和复选框 该方法大致原理如下: 当用户点击label元素时,该label所绑定的input单选框就会被选中,同时通过使用CSS选择器让被选中的input元素之后的label和.content元素都加上相应的样式。 具体如何实现呢?请耐心往下看... 1. HTML结构 <!--HTML--> <ul> <li> <input id="tab1" type="radio" name="tab" checked> <label for="tab1">选项一</label> <div class="content">选项一内容</div> </li> <li> <input id="tab2" type="radio" name="tab"> <label for="tab2">选项二</label> <div class="content">选项二内容</div> </li> <li> <input id="tab3" type="radio" name="tab"> <label for="tab3">选项三</label> <div class="content">选项三内容</div> </li> </ul> 单选框想必大家都会用吧,type为radio,name属性值相同即可。 另外需要注意以下两点: ① label需要绑定input,方法就是label的for属性值与input的id一致,这样当点击label元素时input单选框就会被选中 ② input、label和div三者是有顺序的,不能随意调换顺序(原因后面你就知道了) 2. CSS样式 没有JS,那么CSS自然就得扮演举足轻重的角色。 /*CSS*/ *{ margin: 0; padding: 0;} ul{ position: relative; width: 300px; margin: 100px auto;} ul li{ list-style: none;} ul li input{ display: none;} ul li label{ float: left; width: 100px; text-align: center; line-height: 30px; border: 1px solid #000; border-right: 0; box-sizing: border-box; cursor: pointer; transition: all .3s;} ul li input:checked+label{ color: #fff; background-color: #000;} ul li:last-child label{ border-right: 1px solid #000;} ul li .content{ opacity: 0; position: absolute; left: 0; top: 31px; width: 100%; height: 300px; border: 1px solid #000; box-sizing: border-box; font-size: 24px; text-align: center; line-height: 300px; color: #fff; transition: all .3s;} ul li:nth-child(1) .content{ background-color: #0f0;} ul li:nth-child(2) .content{ background-color: #00f;} ul li:nth-child(3) .content{ background-color: #f0f;} ul li input:checked~.content{ opacity: 1;} 这里同样有几个重点需要注意: ① input需要隐藏,因为我们并不需要显示它,但它却是实现Tab切换的核心力量 ② “input:checked+label” 表示被选中的单选框后的 label 元素需要做标记 ③ .content 元素需要先全部隐藏 ③ “input:checked~.content” 表示被选中的单选框后的 .content 元素需要显示 注: + 表示相邻兄弟选择器,也就是选择紧邻其后的元素; ~ 表示兄弟选择器,也就是选择该元素之后的所有同级元素 方法二::target伪类 关于:target伪类,在我之前文章中有讲到,详情请戳→纯CSS制作单页Web应用 该方法大致原理如下: 当用户点击a元素时,页面URL后会相应添加当前所点击的锚链接,这时对应此锚连接id的.content元素就会应用:target伪类的样式,而同时也给已应用上:target伪类的.content元素之后的a元素自身应用样式。 1. HTML结构 <!--HTML--> <ul> <li> <div class="content" id="content1">选项一内容</div> <a href="#content1">选项一</a> </li> <li> <div class="content" id="content2">选项二内容</div> <a href="#content2">选项二</a> </li> <li> <div class="content" id="content3">选项三内容</div> <a href="#content3">选项三</a> </li> </ul> 这种方式的HTML结构相对比较简单,不需要使用隐藏的单选框作为媒介,而是使用锚链接和:target作为连接“选项”与“选项内容”之间的桥梁。 另外需要注意以下两点: ① 每个a标签的href属性须与其兄弟节点.content元素的id值一致 ② .content元素与a标签的顺序不能更改 2. CSS样式 /*CSS*/ *{ margin: 0; padding: 0;} a{ text-decoration: none; color: #000;} ul{ position: relative; width: 300px; margin: 100px auto;} ul li{ list-style: none;} ul li a{ float: left; width: 100px; text-align: center; line-height: 30px; border: 1px solid #000; border-right: 0; box-sizing: border-box; cursor: pointer; transition: all .3s;} ul li:last-child a{ border-right: 1px solid #000;} ul li .content{ opacity: 0; position: absolute; left: 0; top: 31px; width: 100%; height: 300px; border: 1px solid #000; box-sizing: border-box; font-size: 24px; text-align: center; line-height: 300px; color: #fff; transition: all .3s;} ul li:nth-child(1) .content{ background-color: #0f0;} ul li:nth-child(2) .content{ background-color: #00f;} ul li:nth-child(3) .content{ background-color: #f0f;} ul li .content:target{ opacity: 1;} ul li .content:target+a{ color: #fff; background-color: #000;} 这里同样有几个重点需要注意: ① “.content:target” 将锚链接指向的当前.content元素应用样式 ② “.content:target+a” 将锚链接指向的当前.content元素后紧邻的a元素应用样式 本文重点总结 无需JS,利用单选框或锚链接,再配合一些CSS样式即可制作简单的Tab切换
很久没有写有关微信小程序的文章了,正好最近在做小程序项目,也遇到了一些小问题,与大家分享一下。 一、如何在video、map和canvas等组件上定位元素? 在小程序中video、map和canvas等组件是原生组件,层级是最高的,我们无法通过CSS样式来控制其层级,但是有时候我们确实需要在这些组件上定位元素,比如文字和图片。 这时我们就需要用到小程序自身提供的cover-view和cover-image组件,我们可以分别用它们来放置文字和图片,需要注意的是,cover-view中只能嵌套cover-view和cover-image。例如: /* WXSS */ video{ position: relative; width: 100%;} .text{ position: absolute; bottom: 60rpx; left: 0; right: 0; text-align: center;} <!--WXML--> <video id="myVideo" src="http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400"> <cover-view class='text'>视频文字</cover-view> </video> 最终效果如下: 我们可以看到,video标签中可以嵌套想要在视频上显示的文字,我们需要使用cover-view标签(非view标签)。同样的,如果想要在视频上显示图片,我们就需要使用cover-image标签(非image标签)。当然,有时候我们可能想要嵌套更多文字和图片,那么同样的只能使用cover-view和cover-image组件,否则文字和图片就被视频组件遮挡,无法显示出来。 例如下面的写法就是错误的: <!--WXML--> <video id="myVideo" src="http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400"> <cover-view class='text'><text>视频文字</text></cover-view> </video> 可能你在开发工具上也能正常显示文字,但真机测试你就会发现文字不见了。 正确方式应该同样是将text标签替换成cover-view标签。 <cover-view class='text'><cover-view>视频文字</cover-view></cover-view> 二、video组件固定定位,当滑动页面时,视频会跟着页面滚动如何解决? 最好的解决办法就是不要对video组件进行固定定位,而是直接使用正常的文档流布局方式。换句话说,我们可以使用scroll-view组件来替代view组件。例如: /* WXSS */ #myVideo{ display: block; width: 100%; height: 32vh;} .text{ height: 68vh;} <!--WXML--> <video id="myVideo" src="http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400"></video> <scroll-view class='text' scroll-y> <view>想要学习更多前端知识,欢迎关注微信公众号:前端微站。</view> </scroll-view> 需要注意以下两点: ① video组件与scroll-view组件高度之和应为屏幕总高度,可以直接使用 vh 单位来实现。 ② scroll-view组件需要加上scroll-y属性。 三、自定义组件无法正常使用怎么办? 一般来说遇到自定义组件无法正常使用(组件未正常渲染)有以下几种可能的原因: ① 开发工具或调试基础库版本过低② 未配置组件 ③ 未引入组件 它们分别对应的解决方案很简单,关键是我们得找出具体问题出在哪里。以上三个问题分别对应的解决方案如下: 1. 升级开发工具或更改调试基础库版本 升级开发工具可以直接上微信小程序官方平台上下载。 更改调试基础库版本可以按以下步骤操作: ① 点击开发工具右上角的“详情”按钮 ② 点击按钮展开后找到“调试基础库”,并下拉选择最新基础库(即最顶部选项) 2. 在组件的json文件中添加以下配置代码 { "component": true } 其实就是将该模块声明为组件。 3. 在需要引入组件的页面json文件中添加以下配置代码 { "usingComponents": { "component-tag-name": "path/to/the/custom/component" } } 这步当然就是为了引入组件啦,其中component-tag-name代表组件所在路径,"path/to/the/custom/component"只是示例,需要替换成实际的组件路径。 相关推荐: 微信小程序开发之路(一)微信小程序开发之路(二)微信小程序开发之路(三)微信小程序开发之路(四)
上一节讲到如何使用Webpack实现最基础的打包功能,但是发现以下几个比较突出的问题: ① 我们只打包了一个JS文件,多个文件该如何打包? ② CSS样式该如何打包? ③ 每次写完代码想要看运行结果都需要手动输命令打包,反而降低了开发效率 没关系,本文就是为解决这些问题而来的。继续上节实例,开始吧! 一、JS模块化 使用Webpack成功打包多个JS文件最核心的一点就是使用模块化的开发方式,而Webpack支持CommonJS和ES6两种模块化规范,其中有关CommonJS的语法可以看我之前的一篇文章→JavaScript模块化编程规范。本文也将以CommonJS规范来讲解Webpack中的JS模块化。 我们在scripts目录下添加一个module.js文件,目录结构如下: 并写入以下代码: // module.js var text = 'Hello Webpack!'; module.exports = { // CommonJS规范中模块输出语法 text: text }; 然后在index.js中引入module.js文件: // index.js var module = require('./module.js'); // CommonJS规范中模块引入语法 alert(module.text); // 打包后同样输出“Hello Webpack!” 由于我们在上一节中已经对Webpack的打包入口和输出路径进行了基本配置,所以我们只需同样执行npx webpack命令即可将两个JS文件进行打包输出到dist目录。 小贴士: 有时输入的命令比较长,我们可以将其写入package.json文件的"scripts"属性中。 改写后的package.json如下: { "name": "webpackdemo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.5.0", "webpack-cli": "^2.0.14" } } 接着我们只需执行npm run start命令即可(或直接输入npm start)。 二、打包CSS样式 1. loader简介 由于Webpack打包入口目前只配置了一个index.js文件,那么其他需要被打包的文件都必须通过模块化方式引入该文件才行,而默认情况下,引入的文件必须是js文件(如上面添加的module.js)。 那么其他文件类型该如何进行打包呢? 这时我们就要用到Webpack中所提供的各种loader,它就是专门用于处理除JS文件之外的其他格式文件的编译、提取、合并、打包等工作的。 其中CSS文件的打包需要用到css-loader和style-loader两个loader,css-loader只是用于加载css文件(并没有添加到页面),而style-loader则是将打包后的css代码以<style>标签形式添加到页面头部。 2. 安装loader 输入命令npm i -D css-loader style-loader同时安装这两个loader,安装结束后再在webpack.config.js文件中配置相应的loader,具体配置如下: // webpack.config.js module.exports = { entry: './src/scripts/index.js', // 打包入口 output: { path: __dirname + '/dist', // 输出路径 filename: 'scripts/index.js' // 输出文件名 }, module: { rules: [ { test: /\.css$/, // 正则表达式,表示.css后缀的文件 use: ['style-loader','css-loader'] // 针对css文件使用的loader,注意有先后顺序,数组项越靠后越先执行 } ] } }; 3. 开始打包 在css目录下新建一个style.css文件,并写入样式: /* style.css */ html{ background: #f00;} 然后在index.js中引入该文件: // index.js var module = require('./module.js'); require('../css/style.css'); // 引入css样式 alert(module.text); 最后输入命令npm start,打包完成后打开index.html页面后,你会发现除了弹出之前的“Hello Webpack!”外,页面背景颜色也变为红色,说明样式也已经打包成功。 三、自动化打包 通过上面的讲解你会发现,虽然我们已经将命令改成了npm start,然而实际操作上还是得每次手动输入命令打包,不开心。 我们能不能像以前不用Webpack时那样,写完了直接刷新页面就能看到效果呢? 答案当然是可以的,我们只需在webpack.config.js中添加watch: true就好。 module.exports = { entry: './src/scripts/index.js', output: { path: __dirname + '/dist', filename: 'scripts/index.js' }, module: { rules: [ { test: /\.css$/, use: ['style-loader','css-loader'] } ] }, watch: true // 监听修改自动打包 }; 本文重点总结 ① 打包多个文件为一个文件,必须使用模块化开发方式 ② 先后使用css-loader和style-loader可以打包CSS样式并添加至页面 ③ 想要自动化打包,只需在webpack.config.js文件中添加watch: true
很多还未接触或刚接触Webpack的朋友总会觉得Webpack很难,要安装这个安装那个,要写一大堆的配置,要输一大堆的命令,还要学一大堆的语法,觉得学习成本太高,不用它代码也可以写得很好。是的,但是别担心,本系列教程将会通过一些简单实例带你轻松入门Webpack,相信当你真正掌握它的时候,你一定会爱上它的! 一、Webpack简介 1. 核心 ① 入口(entry) ② 输出(output) ③ loader ④ 插件(plugins) 2. 优势 ① 模块化开发(import,require) ② 预处理(Less,Sass,ES6,TypeScript……) ③ 主流框架脚手架支持(Vue,React,Angular) ④ 庞大的社区(资源丰富,降低学习成本) 详情请戳→我为什么要使用Webpack? 3. 初学者误区 ① 要学会node.js ② 只能用于简单的代码压缩合并 ③ 上线时要将整个项目文件上传服务器 以上几条都是初学者比较容易产生的误区,而事实上…… ① 我们确实需要安装node.js,但基本只是需要它提供的环境及npm而已,你不一定要掌握node.js语法 ② Webpack非常强大,不仅仅可以实现代码的压缩合并,还能进行一些预编译处理以及模块化开发等 ③ 我们只需要将Webpack打包出来的文件目录上传到服务器即可,而不是上传整个项目文件 二、准备工作 1. 安装node.js Webpack本身是基于node.js环境的,所以我们需要先安装node.js,具体方法可以看这里→Node.js——通往全栈之路(一)。 2. 新建项目 ① 创建目录结构 在新创建的项目文件夹下,创建两个基础文件夹:src和dist,其中src是我们开发时源代码所放置的文件夹,dist是我们使用Webpack打包后代码输出的目标文件夹,也就是说最终我们上传服务器的代码都是从dist文件夹下获取。目录结构大致如下: ② 新建一个package.json文件 package.json文件是在node.js环境下开发项目必须要使用到的文件,该文件主要用于配置项目入口、脚本和项目所需安装的依赖等等。我们可以自己手动创建,也可以通过命令行自动创建它。命令行创建很简单,直接在控制台终端输入npm init,然后一直回车即可轻松创建一个最基础的package.json文件。 { "name": "webpackdemo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } 小贴士: 建议使用开发工具中自带的控制台终端,非常方便,自动定位当前项目文件夹,无需手动切换。比如我使用的开发工具是WebStorm,控制台终端如下图: Terminal 3. 安装Webpack 依次输入命令行npm i -D webpack和npm i -D webpack-cli回车进行本地安装,其中i是install的缩写,-D是--save-dev的缩写,也就是说这两个命令行也可以写成npm install --save-dev webpack和npm install --save-dev webpack-cli。 安装后的目录结构如下: package.json文件如下: { "name": "webpackdemo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.5.0", "webpack-cli": "^2.0.14" } } 4. 新建webpack.config.js文件 该文件是Webpack的配置文件,也是项目运行的入口文件,基础写法如下: module.exports = { entry: './src/scripts/index.js', // 需要被打包的js文件路径及文件名 output: { path: __dirname + '/dist', // 打包输出的目标文件的绝对路径(其中__dirname为当前目录的绝对路径) filename: 'scripts/index.js' // 打包输出的js文件名及相对于dist目录所在路径 } }; 三、开始打包 1. 新建需要被打包的js文件 我们给这个index.js文件写入点代码: //index.js alert('Hello Webpack!'); 2. 开始打包 输入命令行:npx webpack,回车。 由于我们已经在webpack.config.js文件配置了打包的相关路径及文件名,所以最终打包后我们就可以在dist目录下看到我们想要的输出结果,打包后整体目录结构如下: 3. 使用打包后的js代码 现在我们已经获得打包后的代码,接下来我们应该使用它,测试它是否能正常运行。 我们可以在dist目录下手动创建一个HTML文件,并引入这个已经打包成功的js文件。目录结构及HTML代码如下: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <script src="scripts/index.js"></script> </body> </html> 打开这个index.html页面后,我们发现浏览器弹出“Hello Webpack!”提示框,说明代码确实已经打包成功。 本文重点总结: 成功运行Webpack基本流程如下: ① 安装node.js ② 创建项目目录和新建package.json ③ 安装webpack和webpack-cli ④ 新建webpack.config.js ⑤ 开始打包你的js代码 结束语: 本文只是对Webpack成功打包js代码的最基础讲解,如需了解更多Webpack相关内容,请持续关注本系列教程,谢谢!
简单讲讲我与前端的故事吧。 刚接触前端时,所有静态资源CSS、图片和JS都是手动引入HTML页面中,这并没有什么不好,想要什么就引入什么嘛。另外,所见即所得,开发好的项目文件直接就可以上传服务器,很方便,因此这样的开发方式一直持续到前不久,也就是开始正式使用Webpack之前。 渐渐地,我开始感觉到jQuery虽然很好用,但是维护起来不是很方便,就是所谓的开发一时爽,维护起来真要命。杂乱无章的代码混在一个文件中,想要寻找某个功能的代码很是困难,要是分开成多个文件引入,又会造成HTTP请求数过多的问题。 于是,我后来选择了Vue。 使用Vue之后的一个好处就是,我不再需要手动去操作DOM了,直接可以将JS变量放到HTML页面中,数据会自动绑定,这对于开发者来说非常方便,我们只需要把重点放到对数据的处理上就可以了,这样代码也精简了很多。 再后来,我发现有些代码在很多地方都要用到,同一个项目中,JS我可以通过定义方法来复用,CSS可以通过定义类名来复用,可是对于HTML,我却无能为力,只能通过复制粘贴的方式…… 所以,我选择了Vue组件。 但是问题接着又来了,我发现Vue组件虽然解决了HTML的复用问题,但实际上只不过是将HTML和JS组合在了一起,CSS依然游离在外,在同一项目中确实都实现了复用,但是当其他项目要使用它时,还得把游离在外的CSS代码复制粘贴过去,这样久而久之自然也是难以忍受了。 幸运的是,单文件的Vue组件正好解决了这个问题。我们可以将HTML、CSS和JavaScript代码放在同一个.vue文件当中,强大的Webpack可以将这些代码分离出来,并分别与其他同类型的代码打包到一起。而我们不需要管Webpack内部是如何运作的,只需要知道单文件组件形式确实会为我们的工作带来极大的便利性。 在CSS方面,习惯使用Less来写CSS代码的我,明显体会到Less模块化所带来的便利性,一个Less文件可以通过引入其他多个Less文件,最后只需将这一个Less文件所编译成的CSS文件引入页面即可。之前我使用的Less编译工具一直都是koala,它是一个可视化的编译软件,可以进行Less代码的编译以及CSS和JS代码的压缩。正因为Less很好用,并且节省了HTTP请求数,然后我就在想,要是JS也能像Less一样模块化引入并且打包成一个JS文件就好了。 正因为有着组件化、模块化和单文件引入的强烈需求,我开始尝试寻找着同时具备这些功能的打包工具,而在尝试过Grunt、Gulp、Webpack和Parcel之后,最终我选择了Webpack。 那么,为什么我会在这些工具中最终选择Webpack呢? 首先,Grunt和Gulp只能算是构建工具,就是将一些CSS和JS文件分别压缩合并成单个文件,当然也具有一些编译功能,比如Less和Sass的编译、ES6到ES5的编译等等。但是Webpack不仅具有它们所具备的这些编译压缩合并功能,同时还具备模块化开发和组件式开发等优点,在目前流行的前端框架React和Vue中也得到很好的支持。 然后再说说Parcel,它一度被人认为是未来最有可能替代Webpack的前端打包工具,主要原因是它几乎零配置,而且打包入口也不仅仅只是JS,另外其打包速度也要比Webpack快。然而,虽然Parcel相比Webpack似乎具有更多优势,但它毕竟还不够成熟,没有Webpack庞大的社区,一旦遇到问题很难在网上快速找到相应解决办法。并且最近Webpack 4.0已经发布,配置相比之前简化了一些,也增加了一些新功能,所以我们完全有理由相信Webpack在未来也会越来越好。 因此,在经过一番体验和对比之后,最终我选择了Webpack。 最后总结一下Webpack的主要优势: ① 模块化开发(import,require) ② 预处理(Less,Sass,ES6,TypeScript……) ③ 主流框架脚手架支持(Vue,React,Angular) ④ 庞大的社区(资源丰富,降低学习成本) 初识Webpack,如有不对之处,欢迎指正,也欢迎一起学习,一同进步!
JS真是博大精深啊,看似相等,其实不等,而看似不等,却是相等。 没错,你没有看错,JS就是这么不可思议! 这个问题涉及到JS基础,通常会出现在前端面试题中,刚开始我对于此问题也是存在疑惑的,但只要明白其原理也很好理解。 一、[]==[]为false 在JS中,数组是属于引用型数据类型,所以“==”左右两边所表示的实际只是数组的所在的地址而已。在创建一个新数组时,其地址均不相同,因此[]==[]最终返回false。 二、[]==![]为true 这个就有点难理解了,按照正常思维来看,符号“!”代表的是取反,所以“==”两边的值应该不等才是。 这个就涉及到了JS中的数据类型转换的问题,当我们使用“==”来对数据进行比较时,若两个数据类型不一致,JS会先按照一定规则将数据转换为同一数据类型后再进行比较。 1. 若其中一个数据类型为数值,那么另一个数据类型会先转换成数值然后再与之比较 ① 字符串与数值 console.log(""==1); // false console.log(""==0); // true console.log("hello"==1); // false console.log("000"==0); // true console.log("666"==666); // true 需要注意的是,"hello"转换为数字是NaN。 ② 布尔值与数值 console.log(true==1); // true console.log(true==2); // false console.log(false==0); // true 很简单,true被转换为1,false被转换为0。 ③ 数组与数值 console.log([]==0); // true console.log([1]==1); // true console.log(["1"]==1); // true console.log([1,2]==1); // false console.log([true]==1); // false 需要注意的是,数组会先通过调用toString()转换为字符串后再转换为数值,比如[true]转换为字符串后为"true",然后再转换为数值是NaN,所以[true]==1返回false。 事实上,数组、对象和函数在与其他基本数据类型进行比较时都会先转换为字符串,然后再转换为相应的数据类型(这里指的数据类型也可能需要转换,总之最后比较时一定是同一数据类型)进行比较,在此就不再一一举例了。 ④ null、undefined和NaN 这三个是个特例,他们比较特殊: a) null==undefined为true;b) null和undefined不会进行数据转换;c) NaN不与任何值相等,甚至包括它自己! 我们来看几个例子: console.log(null==undefined); // true console.log(null==0); // false console.log(null==false); // false console.log(null==""); // false console.log(undefined==0); // false console.log(undefined==false); // false console.log(undefined==""); // false console.log(undefined==NaN); // false console.log(null==NaN); // false console.log(NaN==NaN); // false 2. 若其中一个数据类型为布尔值,则会先将其转换成数值再进行比较 console.log(true=="001"); // true console.log(true==[]); // false console.log(true==["true"]); // false console.log(true==[true]); // false console.log(true==[1]); // true 刚开始接触若不知道其原理,这些其实都很容易判断错误,但知道转换规则之后也比较好判断。 我们可以看到,==左侧均为true,而右侧均不是布尔值类型,所以不管三七二十一,先将true转换成数值1,然后再根据上面 1 中所讲与数值类型比较的转换规则,将右侧值统统进行数据类型转换,转换后如下所示: console.log(1==1); // true console.log(1==0); // false console.log(1==NaN); // false console.log(1==NaN); // false console.log(1==1); // true 3. “!” 为逻辑非,在操作非布尔值类型的数据时,会将该数据类型先转换为布尔值后再取反 console.log(!""); // true console.log(!"false"); // false console.log(!2); // false console.log(![]); // false console.log(!["false"]); // false console.log(!NaN); // true 关于其他数据类型转布尔值,以下均返回true: ① 非空字符串 ② 非零数值 ③ 数组 ④ 对象 ⑤ 函数 理解了以上数据类型转换规则之后在来理解[]==![]的问题就很简单了: ① 将==右侧数组转换为布尔值后再取反,转换后相当于[]==false ② 根据“比较前布尔值转数值”规则,转换后相当于[]==0 ③ 根据“比较前数组遇数值先转字符串后转数值”规则,转换后相当于0==0 本文重点总结 ① 相等操作符,不同数据类型会根据一定规则转换为同一数据类型再比较 ② 数组与数组比较,比较的是其引用 ③ 不同类型比较,遇数值则转数值,布尔值自身转数值 ④ 非空非零引用型,转为布尔均为真
之前写过一篇文章叫JS数组操作之增删改查,今天我就用同样的方式简单介绍一下JS字符串操作中常用函数的用法。 一、增 1. concat() 可以传任何个数的参数,用于字符串的拼接,返回连接后的字符串,而原字符串不受影响。例如: var str1 = 'Hello', str2 = ' World', str3 = '!'; console.log(str1.concat(str2)); // "Hello World" console.log(str1.concat(str2,str3)); // "Hello World!" console.log(str1); // "Hello" 2. "+"号 与concat()功能相同,同样是用于字符串的拼接,例如上面例子可以改成: var str1 = 'Hello', str2 = ' World', str3 = '!'; console.log(str1+str2); // "Hello World" console.log(str1+str2+str3); // "Hello World!" console.log(str1); // "Hello" 可以看出,使用“+”号拼接字符串既方便又直观,所以也是最常用的一种方式。 二、删 1. 裁剪“三剑客” ① substr()第二个参数是裁剪长度,只要为负,裁剪结果必定是空字符串 ② 不管如何裁剪,均不影响原字符串 ③ 当参数为负,slice加总长,substring则归零,substr一加总长一归零。 详情请戳→JavaScript字符串“三剑客” 2. 删空格 ① trim() 与jQuery中的$.trim()方法功能是一样的,用于删除字符串前后所有空格,然后返回结果,而原字符串保持不变。例如: var str = ' Hello World '; console.log(str.trim()); // "Hello World" console.log(str); // " Hello World " ② trimLeft()和trimRight() 这两个方法分别用于删除字符串的左空格和右空格,用法与trim()是一样的,例如: var str = ' Hello World '; console.log(str.trimLeft()); // "Hello World " console.log(str.trimRight()); // " Hello World" 三、改 1. 大小写转换 ① toLowerCase()和toLocaleLowerCase() 这两个方法都可以将字符串中的大写字母全部转换为小写字母,只不过与 toLowerCase() 不同的是,toLocaleLowerCase() 方法会按照本地方式把字符串转换为大写。只有几种语言(如土耳其语)具有地方特有的大小写映射,其他大多情况该方法的返回值与 toLowerCase() 是一样的。例如: var str = 'Hello World'; console.log(str.toLowerCase()); // "hello world" console.log(str.toLocaleLowerCase()); // "hello world" ② toUpperCase()和toLocaleUpperCase() 这两个方法都可以将字符串中的小写字母全部转换为大写字母,用法与toLowerCase()和toLocaleLowerCase()相同,例如: var str = 'Hello World'; console.log(str.toUpperCase()); // "HELLO WORLD" console.log(str.toLocaleUpperCase()); // "HELLO WORLD" 2. 模式匹配 ① replace() stringObject.replace(regexp/substr,replacement) 详情请戳→简述test()、match()、replace()和search()的用法 ② split() ① split() 可以传字符串或正则,也能传第二参数 ② split() 的逆操作是 join() ③ 字符串和数组可以自由转换,所以相当于它们所具备的很多方法是可以通用的 详情请戳→JavaScript字符串的分割 四、查 1. 字符方法 主要有两个: charAt()和charCodeAt(),charAt()是通过索引查找字符串中的某个字符,而charCodeAt()其实就相当于在charAt()查找到字符之后再将其转换为相应的字符编码。例如: console.log(str.charAt(2)); // "l" console.log(str.charCodeAt(2)); // 108 其实查找字符还有一种更加简单直接的方法,就是与数组类似的方括号表示法,例如: console.log(str[2]); // "l" 2. 位置方法 与charAt()正好相反,位置方法是通过某个字符串来查找相应的位置索引,主要有两个: indexOf()和lastIndexOf(),分别为正向查找和反向查找,例如: var str = 'Hello World'; console.log(str.indexOf('l')); // 2 console.log(str.lastIndexOf('l')); // 9 除此之外,这两个方法还能传入第二个参数,表示从字符串中的哪个位置开始查找,例如: var str = 'Hello World'; console.log(str.indexOf('l',3)); // 3 console.log(str.lastIndexOf('l',8)); // 3 可以看出,这两个方法的用法与数组操作中同名的两个方法的用法是一样的。 3. 模式匹配 ① match() stringObject.match(searchvalue) stringObject.match(regexp) ② search() stringObject.search(searchvalue) stringObject.search(regexp) 详情请戳→简述test()、match()、replace()和search()的用法
说完字符串的裁剪,这次来说说字符串的分割。 你可能会有所疑惑,裁剪和分割,这两者到底有什么区别呢? 裁剪: 一次裁剪一部分子字符串并返回,可使用slice()、substring()和substr()方法实现分割: 一次可将字符串分割成多个子字符串并返回由这些子字符串组成的数组,可使用split()实现 好,了解了裁剪和分割的区别之后,我们接下来看看具体如何使用split()方法来实现字符串的分割。 首先,我们先定义一个字符串: var str = 'Hello World!'; 一、只传一个参数 1. 传入非空字符串 console.log(str.split('l')); // [ "He","","o Wor","d!" ] 很简单,我们把字符“l”作为分隔符传入split()方法,最后返回被字符"l"分割成的子字符串组成的数组。 2. 传入空字符串 我们也可以将字符串分割成一个个字母组成的数组,只需给split()传入空字符串即可。 console.log(str.split('')); // [ "H","e","l","l","o","","W","o","r","l","d","!" ] 3. 传入正则表达式 console.log(str.split(/l+/)); // [ "He","o Wor","d!" ] 这个其实就是使用与正则相匹配的子字符串来分割原字符串,而这里匹配到的有两个:“l”和“ll”,所以结果其实就是使用这两个字符串来分割。 二、传入两个参数 第一个参数还是一样,字符串或正则,而第二个参数则是一个数字,用于指定返回数组的大小。例如: console.log(str.split('',5)); // [ "H","e","l","l","o" ] 这实际就是在str.split('')的基础上又截取了输出数组的前5项,与下面两种方式的运行结果是一样的: console.log(str.slice(0,5).split('')); // [ "H","e","l","l","o" ] console.log(str.split('').slice(0,5)); // [ "H","e","l","l","o" ] 这两种方式,一个是先裁后分,另一个则是先分后裁,最终结果都是一样的。而给split()方法传入第二个参数则相当于是个简化版,效果其实是一样的。 三、逆操作方法 说完了split()的用法,再顺便提一提该方法的逆操作方法——join()。 split(): 将字符串分割成数组join(): 将数组合并成字符串 1. 不传参 console.log(str.split('',5).join()); // "H,e,l,l,o" 默认使用逗号来连接数组的每一项而组成字符串并返回。 2. 传入空字符串 console.log(str.split('',5).join('')); // "Hello" 直接将数组中的每一项连接起来组成字符串并返回。 3. 传入非空字符串 console.log(str.split('',5).join('|')); // "H|e|l|l|o" 使用所传字符串参数来连接数组的每一项而组成字符串并返回。 四、活学活用 以上说了这么多,其实真正的内容没多少,关键还是应该掌握如何在实际工作当中去灵活使用。 下面我就随便举个简单的小例子吧~ 如何删除字符串中的某个字符或字符串? 实现这个功能的方法可能有很多,但我个人觉得最简单方便的就是同时使用split()和join()了。 还是使用最开始的字符串,我们下面将所有的字符 “l” 删除。 console.log(str.split('l').join('')); // "Heo Word!" 很简单,先分割后合并,不再赘述。 事实上,只要对数组可以实现的功能,在字符串上也能实现,只要先使用split()将字符串转换成数组,使用数组方法处理过后,再用join()将其转换回字符串即可。 本文重点总结: ① split() 可以传字符串或正则,也能传第二参数 ② split() 的逆操作是 join() ③ 字符串和数组可以自由转换,所以相当于它们所具备的很多方法是可以通用的
JavaScript字符串方法有很多,其中有三个方法与字符串裁剪有关,他们分别是slice()、substring()和substr(),我把他们统称为“三剑客”。 由于他们都是用于裁剪字符串,很容易混淆,所以接下来我将结合具体实例来讲讲他们的共同点和区别。 一、共同点 ① 接受一个或两个参数,其中第一个参数为裁剪的开始位置 ② 都会返回被裁剪下来的子字符串,而原字符串不受影响 ③ 若不传第二个参数,则从开始位置(第一个参数)一直截取到字符串结尾。 var str = '微信公众号:前端微站'; var str1 = str.slice(2); console.log(str1); // "公众号:前端微站" str1 = str.substring(2); console.log(str1); // "公众号:前端微站" str1 = str.substr(2); console.log(str1); // "公众号:前端微站" console.log(str); // "微信公众号:前端微站" 可以看出,当只传入一个参数时,这三个方法的用法和作用都是一致的,都是将“公”字(索引值为2)一直到字符串末尾的字符串裁剪下来并返回,并且都不会影响到原字符串。 二、区别 ① slice()和substring()的第二个参数均表示的是裁剪的结束位置(但不包括该项,这与数组中的slice()方法类似),而substr()的第二个参数则表示的是裁剪下来字符串长度 ② 当传入的参数为负值时,slice()会将所有负参数与字符串的长度相加,substring()会把所有负参数都转换为0,而substr()就相对比较复杂了,它会将第一个负参数加上字符串长度,第二个参数转换为0 1. 参数均为正数 var str = '微信公众号:前端微站'; var str1 = str.slice(2,5); console.log(str1); // "公众号" str1 = str.substring(2,5); console.log(str1); // "公众号" str1 = str.substr(2,3); console.log(str1); // "公众号" 很明显,slice()和substring()用法一致,两个参数分别都表示的是起始位置2和结束位置5,不包含结束位置5所在字符(“:”),而substr()第二个参数表示的是要裁剪下来的字符串长度,实例中是裁剪3个字符。 2. 参数存在负数 var str = '微信公众号:前端微站'; console.log(str.length); // 10 var str1 = str.slice(-4); // 相当于str.slice(6) console.log(str1); // "前端微站" str1 = str.substring(-4); // 相当于str.substring(0) console.log(str1); // "微信公众号:前端微站" str1 = str.substr(-4); // 相当于str.substr(6) console.log(str1); // "前端微站" var str2 = str.slice(2,-4); // 相当于str.slice(2,6) console.log(str2); // "公众号:" str2 = str.substring(2,-4); // 相当于str.substring(2,0),也就是str.substring(0,2) console.log(str2); // "微信" str2 = str.substr(2,-4); // 相当于str.substr(2,0) console.log(str2); // "" var str3 = str.slice(-2,-4); // 相当于str.slice(8,6) console.log(str3); // "" str3 = str.substring(-2,-4); // 相当于str.substring(0,0) console.log(str3); // "" str3 = str.substr(-2,-4); // 相当于str.substr(8,0) console.log(str3); // "" var str4 = str.slice(-4,-2); // 相当于str.slice(6,8) console.log(str4); // "前端" str4 = str.substring(-4,-2); // 相当于str.substring(0,0) console.log(str4); // "" str4 = str.substr(-4,-2); // 相当于str.substr(6,0) console.log(str4); // "" 当参数为负数时,只需牢记,slice见负加总长,substring见负则归零,substr一加总长一归零。 另外还需要特别注意的一点是,slice()第一个参数须小于第二个参数才能正常截取字符串,否则返回的是空字符串,而substring()则没有这个问题。 本文重点总结: ① substr()第二个参数是裁剪长度,只要为负,裁剪结果必定是空字符串 ② 不管如何裁剪,均不影响原字符串 ③ 当参数为负,slice加总长,substring则归零,substr一加总长一归零。
上一节谈到有关Vue.js组件中“传参”的概念,我们使用的是Prop,也就是在标签上自定义属性来向子组件传递数据。这一节我们继续谈谈“传参”,只不过这次不是通过自定义属性,而是通过一个固定的自定义标签<slot>,它有个比较特别的名字:插槽。 同样还是前面的例子,只不过有一点点小改动: <!--HTML--> <pop-tips ref="tips" src="./imgs/success.png"></pop-tips> //pop-tips组件 Vue.component('pop-tips',{ props: { duration: { type: Number, default: 2, validator: function (value) { return value <= 3; } }, src: '' }, data: function(){ return { popShow: false, popText: '' } }, template: '<div class="pop-tips" :class="{show: popShow}"><span><img v-if="src" :src="src" alt=""/>{{popText}}</span></div>', methods: { popTips: function(text){ var _this = this; this.popShow = true; this.popText = text; setTimeout(function () { _this.popShow = false; },this.duration*1000); } } }); // 实例调用组件方法 var vm = new Vue({ el: '#app', methods: { showTips: function(){ popTips('支付成功'); } } }); function popTips(text){ vm.$refs.tips.popTips(text); } 我在提示文字前添加了一张图片,这张图片路径通过Prop传递给子组件,效果如下图: 这效果就是典型的微信弹出提示。当然,若不传src属性,效果就只是纯文字提示: <!--HTML--> <pop-tips ref="tips"></pop-tips> 这就是组件灵活性的好处。那么,插槽到底是个什么东西呢?我们该如何使用它呢? 别着急,前面只是铺垫,接下来请听我娓娓道来~ 同样是以上效果,我们也可以使用插槽来实现它: <!--HTML--> <pop-tips ref="tips"> <!--替代组件中的slot元素--> <img src="./imgs/success.png" alt=""/> </pop-tips> //pop-tips组件 Vue.component('pop-tips',{ //…… template: '<div class="pop-tips" :class="{show: popShow}"><span><slot></slot>{{popText}}</span></div>', //…… }); 最终页面上的渲染出来的HTML内容如下: <div class="pop-tips"><span><img src="./imgs/success.png" alt="">支付成功</span></div> 也就是说组件模板中的<slot>标签是用于指定从父组件传入内容的替换位置,示例中就是将<slot>标签替换成<img>标签。当然,使用插槽有一个好处就是我们可以更加灵活地自定义组件,我们可以传入其他标签,也可以不传任何内容。 同样的,我们也可以给插槽设置默认值: //pop-tips组件 Vue.component('pop-tips',{ //…… template: '<div class="pop-tips" :class="{show: popShow}"><span><slot><img src="./imgs/success.png" alt=""/></slot>{{popText}}</span></div>', //…… }); 也就是将默认值放在<slot>标签中,但是这样做的话,无论你有没有传图片,最终渲染结果都会带有这张默认图片的。 说了这么多,在实际运用当中,什么时候我们应该使用Prop来“传参”,而什么时候我们又应该用<slot>元素呢? 以下是我个人简单的总结: Prop:用于传递简单数据,适合需要自定义内容比较少的情况<slot>: 用于传递标签内容,适合需要自定义内容比较复杂的情况,相对Prop来说更加灵活 所以说,以上所举的例子如果仅仅是为了能够自定义图片的话,完全可以只使用Prop来实现。 本文重点总结: ① 父组件向子组件传递数据最常用方法是Prop和<slot> ② Prop传递的数据是标签属性值,组件内props属性接收数据;<slot>传递的数据是子标签元素,组件内<slot>元素接收数据并且被替换
这次我们讲讲Vue.js中组件中“传参”的概念,也就是父组件向子组件通信的问题,这一点与函数中的参数传递很像。 我们同样使用上篇文章中的例子: <!--HTML--> <pop-tips ref="tips"></pop-tips> //pop-tips组件 Vue.component('pop-tips',{ data: function(){ return { popShow: false, popText: '' } }, template: '<div class="pop-tips" :class="{show: popShow}"><span>{{popText}}</span></div>', methods: { popTips: function(text){ var _this = this; this.popShow = true; this.popText = text; setTimeout(function () { _this.popShow = false; },2000); } } }); 这个例子中,pop-tips文字提示显示出来后会在2秒之后消失,但是如果我想要让它1秒后消失呢? 你可能会说那还不简单,直接把组件中的2000改成1000不就行了嘛。 你作为一个开发组件的人改起来当然容易,但是组件使用者可能不止你一个人哦,其他人可不想关心你组件的具体实现代码! 那么这时候组件的灵活性就显得非常重要,我们能不能像函数传参一样直接给组件传个时间参数就能改变pop-tips组件的显示时长呢? 答案当然是肯定的,例如: <!--HTML--> <pop-tips ref="tips" :duration="1"></pop-tips> <!--传入的参数名为duration,参数值为1,表示1s后消失--> 需要注意的是,这里duration前需要加上v-bind:或者:,因为这里需要传入的是数字1,如果直接写duration="1",那么传入组件的将会是个字符串!! //pop-tips组件 Vue.component('pop-tips',{ props: ['duration'], // 用于接收外部所传入的数据 data: function(){ return { popShow: false, popText: '' } }, template: '<div class="pop-tips" :class="{show: popShow}"><span>{{popText}}</span></div>', methods: { popTips: function(text){ var _this = this; this.popShow = true; this.popText = text; setTimeout(function () { _this.popShow = false; },this.duration*1000); // 调用外部数据时与调用data中数据的方式一致 } } }); 我们可以在定义组件时,给选项对象添加一个props属性,该属性可以是一个数组,其中的元素是组件HTML的部分所定义的自定义属性名称(如示例中的duration属性),这时候在组件内部我们就可以像使用组件内部数据一样使用这些外部传进来的数据了。 但是,这样做可能会造成一些意外的情况。比如说另一个开发者拿到这个组件,但是他并不知道要传这个duration(可能以为它有个默认的时间),或者他一不小心传错了数据类型,等等。 对于这种情况,我们可以对该组件继续进一步的改进,以提高代码的健壮性。例如: //pop-tips组件 Vue.component('pop-tips',{ props: { // 用于接收外部所传入的数据 duration: { type: Number, // 必须是数字类型 default: 2, // 不写该属性时默认为2 validator: function (value) { return value <= 3; // 校验必须小于或等于3 } } }, // …… }); 或者不写默认值,而是强制要求写上duration属性: //pop-tips组件 Vue.component('pop-tips',{ props: { // 用于接收外部所传入的数据 duration: { type: Number, // 必须是数字类型 required: true, // 强制要求写该属性 validator: function (value) { return value <= 3; // 校验必须小于或等于3 } } }, // …… }); 通过以上代码我们可以知道,组件定义中的选项对象的props属性除了可以是数组外,还可以是对象,而对象名就是外部传入的参数名,对象值可以是个对象,其中可以包含参数类型、默认值、是否必传、校验方法等。 有了上面这些限制之后,组件的使用者就得按照这些规则来使用了,一旦不符合某个规则,那么控制台就可能会有相应的警告提示。 结束语: 有关Vue.js组件“传参”今天就讲到这里,总之就是使用props来实现父组件到子组件的数据传递。
在阅读本文之前,请先确认你是否满足以下几个要求: ① 有一定的前端基础 ② 掌握Vue.js最基本的使用(也许从未使用过组件,没关系,这正是本文所要讲的) ③ 对组件化概念有一定的了解 当然,如果你已经对Vue.js组件运用得炉火纯青,那么恭喜你,你也没有必要浪费时间阅读本文了。 接下来,本文将以具体实例讲解以下几个有关Vue.js组件的知识点: ① 如何封装一个组件 ② 组件内数据的存储 ③ 父组件调用执行子组件方法 一、需求 封装一个移动端常用的简单弱提示组件,如下图所示: 当提示出现后,2s之后自动消失。 二、编写全局组件 1. 语法 Vue.component( 组件名称,选项对象 ) 2. 示例代码 /*CSS代码部分省略*/ //Javascript Vue.component('pop-tips',{ data: function(){ return { popShow: false, popText: '' } }, template: '<div class="pop-tips" :class="{show: popShow}"><span>{{popText}}</span></div>', methods: { popTips: function(text){ var _this = this; this.popShow = true; this.popText = text; setTimeout(function () { _this.popShow = false; },2000); } } }); 3. 代码解析 ① pop-tips为组件名称,调用时可以直接使用<pop-tips></pop-tips>。 ② 选项对象中data属性必须是函数形式,并将组件数据对象通过函数返回值形式返回。这点与Vue根实例中data不同,因为组件的主要作用是方便复用,而每个组件在调用时数据是独立的,而并不是共用的。 ③ 选项对象中template属性是个字符串,其中放的是组件HTML模板内容。 ④ 选项对象中methods属性是个由函数组成的对象,这与Vue根实例中的methods属性基本一致。 三、组件调用 函数定义是为了最终的执行调用,同样的,组件定义好了,我们也得知道如何去使用它。 <!--HTML--> <article id="app"> <input type="button" value="点击学习更多前端知识" @click="showTips"/> <!--组件调用--> <pop-tips ref="tips"></pop-tips> </article> //Javascript var vm = new Vue({ el: '#app', methods: { showTips: function(){ this.$refs.tips.popTips('请关注微信公众号:前端微站'); } } }); 上面代码中this.$refs.tips获取的是pop-tips组件实例(其中tips是我们在组件调用时标签上设置的ref属性值),获取到组件实例之后我们便可以取得其中的任意数据和方法。 当然,你可能会觉得每次调用这个组件方法时写这么长一串太麻烦,没关系,我们可以再封装一个全局方法: function popTips(text){ vm.$refs.tips.popTips(text); } 那么上面的弱提示代码就可以写成: popTips('请关注微信公众号:前端微站'); 最终的运行效果就是,当点击“点击学习更多前端知识”这个按钮时,弹出“请关注微信公众号:前端微站”弱提示,并且弱提示在2秒后自动消失。 结束语: 本文对Vue.js组件的讲解可能还不够细致,不够深入,示例也相对比较简单,如有不足之处希望大家评论指出。谢谢!
上次说到CSS3中的线性渐变,表面上看起来似乎有些复杂,但是逐条分解下来其实也没有那么难理解,总结下来无外乎就三点:颜色、方向和位置。那么今天所要讲到的径向渐变相对来说会更复杂一些,不过没有关系,我们同样从最简单的开始说起。 在开始之前,同样先来看看今天的主角: radial-gradient()。 语法: <radial-gradient> = radial-gradient([ [<shape> || <size>] [ at <position> ]? , | at <position>, ]?<color-stop>[ ,<color-stop>]+) // 圆心位置<position> = [ <length> | <percentage> | left | center | right ]? [ <length> | <percentage> | top | center | bottom ]? // 渐变形状<shape> = circle | ellipse // 渐变大小<size> = <extent-keyword> | [ <circle-size> || <ellipse-size>]<extent-keyword> = closest-side | closest-corner | farthest-side | farthest-corner<circle-size> = <length><ellipse-size> = [ <length> | <percentage> ]{2}<shape-size> = <length> | <percentage> // 渐变颜色及颜色位置<color-stop> = <color> [ <length> | <percentage> ]? 以上总结下来就是,radial-gradient() 参数的组成部分主要包括五大部分:形状、大小、圆心位置、颜色和颜色位置。 接下来我将以具体实例对这五大部分逐一进行讲解。首先,定义一个200*150的矩形: width: 200px; height: 150px; 一、传入两个或多个颜色参数 同样以红色到黄色渐变为例: background: radial-gradient(#f00,#ff0); 显示效果很简单,就是一个以矩形中心点为圆心、矩形宽高为横纵向直径、颜色由红到黄向外的渐变: 图一 当然,与线性渐变类似的,同样可以传入更多的颜色参数,例如: background: radial-gradient(#f00,#ff0,#0f0); 图二 二、传入颜色位置参数 该参数紧跟与颜色值之后,例如: radial-gradient(#f00 0,#ff0 100%); 以上效果同图一,同样可以类比线性渐变,具体不再赘述。 三、传入渐变形状参数 渐变形状有两种:圆(cicle)和椭圆(ellipse)。例如: background: radial-gradient(circle,#f00 0,#ff0 100%); 显示效果是一个圆形状的渐变: 图三 background: radial-gradient(ellipse,#f00 0,#ff0 100%); 显示效果是一个椭圆形状的渐变: 图四 四、传入渐变大小参数 1. 具体数值(或百分比) 除了可以像上述显式地声明渐变形状,我们也可以通过传入渐变大小参数来确定形状,例如: background: radial-gradient(60px,#f00 0,#ff0 100%); 此处只传入一个大小参数,则表示该渐变形状为圆,并且半径大小为60px,效果如下: 图五 若传入两个大小不同的参数,则表示该渐变形状为椭圆,例如: background: radial-gradient(50% 60px,#f00 0,#ff0 100%); 效果为长半轴为100px(元素宽度的50%)、短半轴为60px的椭圆形渐变: 图六 我们也可以在声明渐变形状的同时在其后紧跟渐变大小,中间用空格隔开,例如: background: radial-gradient(circle 60px,#f00 0,#ff0 100%); background: radial-gradient(ellipse 50% 60px,#f00 0,#ff0 100%); 其效果分别同图五和图六。 需要特别注意的是,若渐变形状为圆形,则该渐变大小不能为百分数,而椭圆既可以为具体数值也可以为百分数,个人认为或许是因为圆形半径若为百分数的话就无法确定是以元素的宽为标准还是以高为标准了。 2. 大小声明 一共有以下四种: closest-side (指定径向渐变的半径长度为从圆心到离圆心最近的边) closest-corner (指定径向渐变的半径长度为从圆心到离圆心最近的角) farthest-side (指定径向渐变的半径长度为从圆心到离圆心最远的边) farthest-corner (指定径向渐变的半径长度为从圆心到离圆心最远的角) 以 closest-side 为例: background: radial-gradient(circle closest-side,#f00 0,#ff0 100%); 效果如下图所示: 图七 五、传入渐变圆心位置参数 1. 具体数值(或百分数) background: radial-gradient(circle farthest-side at 0 0,#f00 0%,#ff0 100%); 效果为圆心位置位于元素左上角,半径为元素宽度的圆形渐变: 图八 2. 方位名称 background: radial-gradient(circle closest-side at center,#f00 0,#ff0 100%); 以上代码效果同图七。 background: radial-gradient(circle farthest-side at top left,#f00 0,#ff0 100%); 以上代码效果同图八。 注意: 圆心位置参数一定要置于radial-gradient()第一个参数的末尾,顺序千万不能放反了哦~~ 六、重复渐变 虽然上面已经讲完径向渐变的五大组成部分,但是,与线性渐变一样,径向渐变也同样还存在着重复渐变,我们可以用repeating-radial-gradient()来实现。例如: background: repeating-radial-gradient(circle at center,#f00 0,#f00 10%,#ff0 10%,#ff0 20%); 效果如下图所示: 图九 嗯,这靶子效果不错!嘿嘿…… 结束语: 好了,有关CSS3中的径向渐变今天就讲到这里,如有疑问,欢迎大家评论留言哦~~
今天来谈谈CSS3中的线性渐变,有了这个,好多以前必须要用图片才能实现的效果如今可能只需简单一行代码就能实现。 首先看看今天的主角: linear-gradient()。 它,实际上不是颜色,而是背景图片。也就是说,它并不是color的属性值,而是background的属性值。 我们接下来看看linear-gradient()的具体用法。 语法: <linear-gradient> = linear-gradient([ [<angle> | to <side-or-corner> ] ,]? <color-stop>[,<color-stop>]+)<side-or-corner> = [left | right] || [top | bottom]<color-stop> = <color>[<length>|<percentage>]? 说明: <angle>: 用角度值指定渐变的方向(或角度)。 to left:设置渐变为从右到左。相当于: 270deg to right:设置渐变从左到右。相当于: 90deg to top:设置渐变从下到上。相当于: 0deg to bottom:设置渐变从上到下。相当于: 180deg。这是默认值,等同于留空不写。 <color-stop> 用于指定渐变的起止颜色: <color>: 指定颜色。 <length>:用长度值指定起止色位置。不允许负值 <percentage>:用百分比指定起止色位置。 以上语法部分使用的是正则表达式的语法规则,可能并不是很好理解。 没关系,学习都是需要循序渐进的,我们从最简单的开始…… 1. 传入两个或多个颜色参数 这个很好理解,要做渐变,当然得设置颜色值,换句话说,你得让浏览器知道你想要实现从哪个颜色到哪个颜色之间的渐变。例如,你要实现从红色到黄色之间的渐变,你可以这样写: background: linear-gradient(#f00,#ff0); 默认是从上往下渐变的,效果如下: 图一 如果你还想要加入更多的颜色,继续往后面添加颜色参数即可,例如: background: linear-gradient(#f00,#ff0,#0f0); 效果是根据参数顺序依次往下渐变: 图二 2. 传入渐变方向参数 有时候我们不只是想要从上而下固定方向上的颜色渐变,可能想要实现的是各个方向上的渐变,那么此时我们可以在 1 中传入多个颜色值的基础上再传入渐变方向,该参数作为第一个参数,可以是八种方向,也可以是具体的角度值。 我们来看具体实例: background: linear-gradient(to right,#f00,#ff0); 根据第一个参数语义就可以知道,这里实现的是从左往右方向上的由红到黄的渐变,效果如下所示: 图三 如果想要实现从左下角往右上角的渐变,那么第一个参数可以写成to right top或者直接写具体角度值45deg,上面例子就可以这样写: background: linear-gradient(to right top,#f00,#ff0); 或者: background: linear-gradient(45deg,#f00,#ff0); 效果如下所示: 图四 需要注意的是,角度值若为0deg表示的是从下到上的渐变(方向表示为to top),所以45deg则表示顺时针旋转45度。 3. 传入渐变颜色起止位置参数 默认情况下的起止位置是从0%到100%,也就是说 1 中例子实际上还可以写成下面这样(效果如图一): background: linear-gradient(#f00 0%,#ff0 100%); 注意位置参数须紧跟在颜色值之后,中间用空格隔开。 若是三种颜色就是这样的(效果如图二): background: linear-gradient(#f00 0%,#ff0 50%,#0f0 100%); 当然,我们还可以写成具体数值,如果元素高度为150px,那么就可以写成这样: background: linear-gradient(#f00 0,#ff0 75px,#0f0 150px); 另外,渐变颜色起止位置参数还有一个很重要的用法,就是可以在同一个元素中写出多种不同层次颜色效果。 我们来看一个具体实例: background: linear-gradient(#f00 0%,#f00 50%,#ff0 50%,#ff0 100%); 显示效果如下图: 图五 再比如: background: linear-gradient(45deg,#f00 0%,#f00 33.33%,#ff0 33.33%,#ff0 66.66%,#0f0 66.66%,#0f0 100%); 图六 表面上看代码挺吓人的,实际上只要理解了前面内容的话也是很好理解的。 为了让大家都能更好的理解,我们来一步步分解这些参数吧~~ ① 45deg(渐变方向) 这个很好理解,45deg则代表的是从元素左下角到右上角的渐变,可以用to right top替代之。 ② #f00 0%,#f00 33.33%(红色色块) 由红色渐变到红色,当然就是纯红色咯~ 0%和33.33%则代表的是相应颜色所在的位置。 ③ #ff0 33.33%,#ff0 66.66%(黄色色块) 原理同上。 ④ #0f0 66.66%,#0f0 100%(绿色色块) 原理同上。 这样一分解,是不是看起来清晰多了呢? 4. 重复渐变 有时候我们可能需要制作斑马条纹状的图案效果,类似下图这样: 图七 这样的效果我们完全可以利用 3 中的方式一点点去写,但是显然重复工作量比较大。 对于这样有规律的图案,我们可以使用repeating-linear-gradient()来实现,用法与linear-gradient()完全相同。图七效果可以这样来写: background: repeating-linear-gradient(#f00 0,#f00 10%,#ff0 10%,#ff0 20%); 也就是说,我们只需要写出其中一部分图案,其他部分将会被自动平铺。 另外,由于linear-gradient()本身也是背景图片,所以以上效果我们还可以使用背景图片平铺方式来实现: background: linear-gradient(#f00 0,#f00 50%,#ff0 50%,#ff0 100%); background-size: 100% 20%; 需要注意的是此时需要使用background-size来设置渐变图案的大小。 结束语: 好了,有关CSS3中的线性渐变今天就讲到这里,如有疑问,欢迎大家评论留言哦~~
一、Swiper.js的loop模式下,如何正确获取索引值? Swiper.js,相信作为前端开发者的你一定知道它吧。它几乎可以用来制作任何形式的轮播图,非常方便和实用。 有时候,我们需要动态获取当前轮播图的索引值,常常我们会使用activeIndex属性来获取。比如像这样: var mySwiper = new Swiper('.swiper-container'); $('#btn1').click(function(){ alert(mySwiper.activeIndex); }) 然而,当轮播图需要设置循环时,我们就不能使用activeIndex属性了,而是要使用realIndex属性,例如: var mySwiper = new Swiper('.swiper-container',{ loop:true, }); $('#btn1').click(function(){ alert(mySwiper.realIndex); }); 二、word-break: break-all与word-wrap: break-word的区别是什么? 表面上看,这俩家伙长得太像,很多人都很难区分它们,包括我自己也是到现在才把它们彻底弄明白的。 首先我们先来说说它们的共同点吧! 你瞧它俩中都含有word和break关键词,也就是说它们都与单词内断句有关。 而它们之间的区别具体又是什么呢? 同样我们可以从表面文字上分析: word-break: break-all: break-all,顾名思义,不管什么情况,只要单词在行尾放不下就断开单词,将剩余内容换行至下一行。word-wrap: break-word: 表示单词在行尾放不下就换行至下一行,如果下一行还是放不下那才断开单词。 还是举两个例子吧~~ <!--HTML--> <p>I'd like to know how to distinguish "word-break: break-all" from "word-wrap: break-word". blablablablablablablablabla...</p> 1. word-break: break-all (总是尽量填满一整行) /*CSS*/ p{ width: 200px; word-break: break-all;} word-break: break-all 2. word-wrap: break-word(只有一个单词长到比容器宽度更大时单词才会断开) /*CSS*/ p{ width: 200px; word-wrap: break-word;} word-wrap: break-word 通过上面两个例子你应该能区分word-break: break-all与word-wrap: break-word了吧? 三、如何使用纯CSS禁止鼠标点击事件? 想要禁止鼠标点击事件,我们常常会使用JS中事件对象的preventDefault()方法来禁止,例如禁止一个普通链接的跳转: <!--HTML--> <a id="baidu" href="http://www.baidu.com">百度一下,你就知道。</a> //JavaScript document.getElementById("baidu").onclick = function(e){ e.preventDefault(); } 然而,事实上,我们只需简单一句CSS代码就能实现以上同样的效果: /*CSS*/ #baidu{ pointer-events: none;} 这时候,如果你再在该 a 元素添加任何点击事件都会失效…… 怎么样?是不是超简单? 四、如何给文字设置两端对齐? 这个就简单提一下吧,因为平时我们用得最多的文字对齐方式无非就是以下几种: 左对齐: text-align: left 右对齐: text-align: right 居中对齐: text-align: center 其实还有一个也是非常实用的: 两端对齐: text-align: justify 我们接下来看看默认情况和加上text-align: justify之后的区别。 ① 默认情况,也就是加上 text-align: left 之后 text-align: left 右边参差不齐,作为完美主义者的我可受不了这样的,我要的是下面这种效果。 ② 加上 text-align: justify 之后 text-align: justify 左右两端文字平整对齐,完美! 以上是我在工作当中所遇到的一些问题总结,在此与大家共勉!
之前写过一篇有关Canvas图片处理的文章,今天我们讲讲如何使用Canvas来压缩图片。 Canvas图片压缩流程 接下来我将以具体实例为大家讲解Canvas图片压缩的具体流程。 一、本地图片输入 1. 获取本地文件 <!--HTML--> <input type="file" id="choose-img" /> // JS var chooseImg = document.getElementById("choose-img"); chooseImg.onchange = function(e){ var file = this.files[0]; // …… (省略部分代码后续依次展示,下同) }; 很简单,就是通过type类型为file的按钮来获取本地文件。 2. 判断所获取的本地文件类型 <!--HTML--> <div id="result"></div> // JS var result = document.getElementById("result"); // 用于显示图片输出结果,或者错误提示 if(/image/.test(file.type)){ // 判断文件类型是否为图片 // …… } else{ result.innerHTML = '<span style="color: red;">文件类型有误!</span>'; } 3. 将所获取的本地图片以base64格式输出 var img = new Image(), // 创建图片对象,用于放置原始图片 reader = new FileReader(); reader.readAsDataURL(file); // 以base64格式读取并存入FileReader对象的result属性中 reader.onload = function(){ img.src = this.result; // 将图片base64字符串直接赋予Image对象的src中 document.body.insertBefore(img,chooseImg); // 将输出的图片插入到文件按钮之前 img.onload = function(){ // …… }; }; 二、在Canvas画布中绘制图片 1. 创建画布 var canvas = document.createElement('canvas'); canvas.width = img.clientWidth; canvas.height = img.clientHeight; var context = canvas.getContext('2d'); 注意:画布大小与所输入图片宽高相同。 2. 绘制图片 context.drawImage(img,0,0,canvas.width,canvas.height); 三、压缩图片并输出 <!--HTML--> 图片压缩比率 : <input id="rate" type="number" min="0" max="100" /> % // JS var rate = document.getElementById("rate").value || 100; // 输入图片压缩比率,默认为100% var imgUrl = canvas.toDataURL(file.type,rate/100); // 第一个参数为输出图片类型,第二个为压缩比 result.innerHTML = '压缩后:<img src="'+ imgUrl +'" />'; // 将压缩后的图片置于result中显示 img.style.display = 'none'; // 将原始图片隐藏 将在Canvas画布中所绘制的图片再次以base64格式输出。 四、完整代码展示 <!--HTML--> 图片压缩比率 : <input id="rate" type="number" min="0" max="100" /> % <input type="file" id="choose-img" /> <div id="result"></div> // JS var chooseImg = document.getElementById("choose-img"), result = document.getElementById("result"); chooseImg.onchange = function(e){ var file = this.files[0]; if(/image/.test(file.type)){ var img = new Image(), reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(){ img.src = this.result; document.body.insertBefore(img,chooseImg); img.onload = function(){ var canvas = document.createElement('canvas'); canvas.width = img.clientWidth; canvas.height = img.clientHeight; var context = canvas.getContext('2d'); context.drawImage(img,0,0,canvas.width,canvas.height); var rate = document.getElementById("rate").value || 100; var imgUrl = canvas.toDataURL(file.type,rate/100); result.innerHTML = '压缩后:<img src="'+ imgUrl +'" />'; result.style.display = 'block'; img.style.display = 'none'; }; }; } else{ result.innerHTML = '<span style="color: red;">文件类型有误!</span>'; } }; 经测试发现,通过Canvas压缩JPEG格式图片效果最佳,PNG压缩效果不明显,有时反而变得更大。
之前讲了这么多的原生JS,这次换换口味吧,讲讲曾经风靡一时的JS库——JQuery。 一、jQuery事件绑定的方法 1. 直接使用事件方法 【jQuery对象】.【事件名】(【方法】) 比如给一个按钮添加点击事件: <!--HTML--> <input type="button" id="btn" value="按钮" /> //JS $('#btn').click(function(){ console.log('你点击了按钮!'); }); 2. bind() 【jQuery对象】.bind("【事件名】",【方法】) 上面例子中的JS代码可以写成下面这样: //JS $('#btn').bind("click",function(){ console.log('你点击了按钮!'); }); 相应的取消绑定可使用unbind()方法。 3. on() 【jQuery对象】.on("【事件名】",【方法】) 上面例子中的JS代码可以写成下面这样: //JS $('#btn').on("click",function(){ console.log('你点击了按钮!'); }); 相应的取消绑定可使用off()方法。 需要注意的是,on()和off()是从jQuery1.7+版本才开始有的。 二、jQuery事件绑定的种类 1. 多个选择器绑定同一个事件 $("【选择器1】,【选择器2】").on("【事件名】",【方法】) <!--HTML--> <input type="button" id="btn1" value="按钮一" /> <input type="button" id="btn2" value="按钮二" /> //JS $('#btn1,#btn2').on('click',function(){ console.log('你点击了按钮!'); }); 2. 多个事件绑定同一个方法 【jQuery对象】.on("【事件名1】 【事件名2】",【方法】) <!--HTML--> <input type="button" id="btn" value="按钮" /> //JS $('#btn').on('click mouseover',function(){ // 这时候鼠标移入按钮或点击按钮都会执行后面的方法 console.log('你点击了按钮!'); }); 3. 多个事件绑定不同方法 【jQuery对象】.on({ "【事件名1】" :【方法1】, "【事件名2】" :【方法2】}) <!--HTML--> <input type="button" id="btn" value="按钮" /> //JS $('#btn').on({ 'click': function(){ console.log('你点击了按钮!'); }, 'mouseover': function(){ console.log('你移入了按钮!'); } });
我们经常可能会遇到这样一个情况: 在一个固定宽高的盒子中,要放置一张宽高比不定的图片(比如说后台上传的图片),这时候图片应该如何设置样式呢? 有人可能会说,那还不简单,图片宽高设置成父级盒子的宽高不就行了? 举个例子: /*HTML*/ <div class="image"> <img src="imgs/img.jpg"/> </div> /*CSS*/ .image{ width: 400px; height: 200px;} .image img{ width: 100%; height: 100%;} 结果很显然,如果图片比例与父级盒子比例不对,则很容易造成图片变形: 图一 所以我们往往会直接不限制图片的高度,让图片溢出部分隐藏。 /*CSS*/ .image{ width: 400px; height: 200px; overflow: hidden;} .image img{ width: 100%;} 图二 但是这并不是最佳的显示效果,最佳效果应该是让整个图片居中放置。所以有些人可能会用JS去计算,而有些人直接就用背景图片替代,因为背景图片可以直接设置background-size: cover,效果就像这样: 图三 然而,事实上,还有一种既可以不用JS,又可以不用背景图片的完美解决方案。 那就是使用CSS中的object-fit属性。 一、object-fit 属性的用法介绍 该属性一般作用于图片或视频标签上,主要有以下五种属性值: fill(不保持纵横比缩放图片,使图片完全适应)contain(保持纵横比缩放图片,使图片的长边能完全显示出来)cover(保持纵横比缩放图片,只保证图片的短边能完全显示出来)none(保持图片宽高不变)scale-down(当图片实际宽高小于所设置的图片宽高时,显示效果与none一致;否则,显示效果与contain一致) 当然还有三个基本上不用的属性,它们显示的效果相当于未设置该属性,本文不做细讲。 inherit initial unset 接下来以具体实例分别讲讲以上五个主要的属性值。 /*HTML*/ <img src="imgs/img.jpg"/> 1. fill /*CSS*/ img{ width: 400px; height: 200px; object-fit: fill;} 图片可能变形,效果同图一。 2. contain /*CSS*/ img{ width: 400px; height: 200px; object-fit: contain; background: #eee;} 类似于background-size: contain,图片可以完整显示,如图四。 图四 3. cover /*CSS*/ img{ width: 400px; height: 200px; object-fit: cover;} 类似于background-size: cover,图片会被裁切(只有当图片实际宽高比与样式设置的宽高比正好一致时才不会被裁切),效果同图三。 4. none 图片 宽高 保持不变,可能出现以下两种情况。 ① 图片实际宽度大于样式设置的宽度(或图片实际高度大于样式设置的高度) /*CSS*/ img{ width: 400px; height: 200px; object-fit: none;} 图片会被裁切,如图五。(图片实际宽高为512*512) 图五 ② 图片实际宽度小于样式设置的宽度(或图片实际高度小于样式设置的高度) /*CSS*/ img{ width: 600px; height: 600px; object-fit: none; background: #eee;} 图片不会被裁切,如图六。 图六 5. scale-down 图片 宽高比 保持不变,同样也可能出现以下两种情况。 ① 图片实际宽度大于样式设置的宽度(或图片实际高度大于样式设置的高度) /*CSS*/ img{ width: 400px; height: 200px; object-fit: scale-down; background: #eee;} 效果与 object-fit: contain 一致,如图四。 ② 图片实际宽度小于样式设置的宽度(或图片实际高度小于样式设置的高度) /*CSS*/ img{ width: 600px; height: 600px; object-fit: scale-down; background: #eee;} 效果与 object-fit: none 一致,如图六。 二、object-position 属性的用法介绍 object-position属性规定了指定元素的替换内容在其盒子内的对齐方式,与 background-position 类似,默认为居中,该属性实际用到的情况比较少,本文不再具体展开。 三、object-fit与object-position属性的兼容性 兼容性整体还可以,移动端基本没什么问题,只可惜还是IE……
我正在参加怦然心动·邂逅你的11封情书——1111情书交友创作大赛,快来给我写情书吧。 昵称: 璿而不华 地点: 魔都 职业: IT互联网 自荐文章: 摆脱晚睡拖延症其实很简单…… 自述: 大学毕业两年半,目前从事互联网。 时常有些文艺范,琴棋书画样样行。 洗衣做饭全都会,不打游戏少追剧。 若问性格现何如?要说正经也能污。 爱好: 1. 绘画 从小一直坚持的爱好,虽不常画,但每一副都很认真。 立花泷 宫水三叶 宫水三叶 泷和三叶.jpg 这是我超喜欢的一部动漫——《你的名字。》。 你好,我想知道你的名字是? 2. 吉他 我的吉他 吉他刚开始学没多久,仅仅是爱好,目前弹得并不好。 3. 摄影 上海科技馆 说实话,不能说自己技术高,只能说相机好。 4、PS 渴望回到现实的猫 自学PS多年,目前在工作中受益无穷。 情书: 我想牵着你的手,一起体验琴棋书画; 我想牵着你的手,一起阅尽世间繁华; 我想牵着你的手,一起去坐旋转木马; 我想牵着你的手,一起走遍海角天涯。 1111愿望: 早日找到心仪的另一半,但不将就。 一直保持健康的体魄和良好的身材。 工作上突破瓶颈,升职加薪。 自己和家人永远健康快乐。
一、什么是FileReader类型? FileReader类型实现的是一种异步文件读取机制,通常可以用于读取文本文件和图片文件,而本文只单独讲讲文本文件的读取。 使用FileReader读取文本文件内容主要用到以下方法、事件和属性: 方法: readAsText() 事件: load 属性: result 二、结合具体实例讲解文字读取流程 1. HTML结构 主要用到input元素,type属性值为file,用于从本地获取文件。 <input type="file" id="file" /> 通过以上代码在选择文件时一次只能选择一个,若要一次选择多个文件,可在标签上加上multiple="multiple"属性值,或者直接写multiple。 <input type="file" id="file" multiple="multiple" /> 或者: <input type="file" id="file" multiple /> 2. 给input元素加上change事件 var file = document.getElementById("file"); file.addEventListener('change',function(){ //........ }); 当我们点击按钮并成功选择文件时会执行以下 3 中的代码。 3. 读取文本文件中的文字内容 ① 获取已从本地选择的文件 var fileVal = this.files[0]; // 获取所选文件中的第一个文件 这里用到了 File API,每个 File 对象对应一个文件,每个 File 对象有下面几种属性: name: 本地文件名 size: 文件的字节大小 type: 文件的 MIME 类型 lastModifiedDate: 文件上一次被修改的时间 比如想要获取文件名可以这样写: var fileName = this.files[0].name // 获取到的文件名中包含文件后缀 ② 新建 FileReader 对象 var reader = new FileReader(); ③ 读取文件中的文字内容 reader.readAsText(fileVal,'gb2312'); readAsText() 用于将文件中的内容以纯文本的形式读取,读取到的文本会保存在 result 属性中(注意该方法执行没有返回值),可传入两个参数:文件对象和文本编码类型。 注意: 这里第二个参数最好写上'gb2312'编码类型,否则可能出现文字乱码问题。 ④ 文件读取成功后输出文本内容 reader.onload = function(){ var text = this.result; console.log(text); }; 通过 readAsText() 方法读取到的纯文本内容保存在 result 属性中。 三、最终完整实例代码 为了让大家能够从整体上更加清晰的了解文字读取的流程,在此贴上以上实例中的完整代码: <!--HTML部分--> <input type="file" id="file" multiple /> // JavaScript部分 var file = document.getElementById("file"); file.addEventListener('change',function(){ var fileVal = this.files[0]; var reader = new FileReader(); reader.readAsText(fileVal,'gb2312'); reader.onload = function(){ var text = this.result; var p = document.createElement('p'); p.innerHTML = text.split('\n').join('<br>'); document.body.appendChild(p); }; }); 以上代码中,text.split('\n').join('<br>')的作用是将文本文件中的换行符转换为<br>,\n代表文本回车换行。
当一个网站复杂度较高需要多人协作开发时,传统的非模块化编程模式容易导致代码冲突和依赖等问题,而模块化编程的诞生正是为了解决此类问题。然而,在ES6之前,原生JavaScript是不支持模块化的,因此就出现了一系列的JavaScript库来实现此功能,这些库主要遵循以下三种规范: ① CommonJS ② AMD ③ CMD 接下来我就粗略地讲讲这三种规范。 一、CommonJS规范 关键词: module,exports,require CommonJS规范下的模块调用是同步的,也就是说必须等模块加载完成之后,接下来的代码才能继续运行。因此,该规范主要适用于服务端,因为服务端可以直接从硬盘中调用所需要的模块,而这个过程速度是非常快的。相比之下,通过网络调用所需模块的浏览器客户端则不适合使用该规范。 目前使用该规范的典型代表有:Node.js、微信小程序。 下面以Node.js中的两个小例子,简单讲讲CommonJS规范下的模块化编程。 1. 无返回值的模块调用 //module.js console.log('这是一个模块。'); //main.js require('./module'); // 或写成 require('./module.js'),但千万注意不能写成 require('module') 以上module.js和main.js两个文件处于同一目录下。 2. 有返回值的模块调用 //module.js function foo(){ console.log('这是一个模块。'); } module.exports = { // 此处提供模块对外接口 foo: foo // 此处对外接口中的方法名不一定要与以上定义的方法名一致,比如可以写成 func: foo,那么此时调用时就应该写成 module.func() }; //main.js var module = require('./module.js'); // 加载module模块 module.foo(); // 此处调用module模块下的foo方法,该方法名须与模块中对外接口方法名一致 二、AMD规范 关键词: define,require 与CommonJS不同,AMD规范下的模块调用是异步的,主要适用于浏览器客户端。 目前使用该规范的典型代表有:require.js、curl.js。 下面以require.js为例,简单讲讲AMD规范下的模块化编程。 <!--HTML--> <script src="scripts/require.js"></script> <script src="scripts/main.js" data-main="scripts/main"></script> 1. 无返回值的模块调用 //module.js console.log('这是一个模块。'); //main.js require(['scripts/module']); // 请求的模块路径用数组表示 2. 有返回值的模块调用 //module.js function foo(){ console.log('这是一个模块。'); } define(function(){ return { foo: foo } }); //main.js require(['scripts/module'],function(module){ module.foo(); }); 三、CMD规范 关键词: use,define,require,exports,module CMD规范结合了以上两种规范的特点,既可以同步调用,也可以异步调用,在语法上也非常相似。 目前使用该规范的典型代表有:sea.js。 下面就以sea.js为例,简单讲讲CMD规范下的模块化编程。 <!--HTML--> <script src="scripts/sea.js"></script> <!--引入主模块,模块根目录为sea.js所在目录,有点类似于C语言中的main函数--> <script type="text/javascript"> seajs.use('main'); </script> 1. 无返回值的模块调用 //module.js console.log('这是一个模块。'); //main.js define(function(require,exports,module){ require('module'); }); 这里需要重点说明一下,define()中回调函数中所传参数名称不允许修改。 2. 有返回值的模块调用 定义模块: //module.js function foo(){ console.log('这是一个模块。'); } define(function(require,exports,module){ //也可以直接通过return方式暴露模块接口,这样就与AMD规范相同 module.exports = { foo: foo } }); 调用模块存在同步和异步两种方式: ① 同步调用 //main.js define(function(require,exports,module){ var module = require('module'); module.foo(); }); ② 异步调用 //main.js define(function(require,exports,module){ require.async('module',function(module){ module.foo(); }); }); 本次有关JavaScript模块化编程规范的分享就到这里,若有不到之处,欢迎指正,谢谢!
一、Node.js简介 Node.js是一个基于Chrome JavaScript运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js使用事件驱动, 非阻塞I/O模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。 以上是百度百科上对Node.js的介绍。简而言之,Node.js基于JavaScript语言,并且运行于服务端,主要用于处理后端业务逻辑。 二、Node.js的下载与安装 可以直接前往其官网进行下载,下载时注意选择与自己电脑系统相匹配的项,比如你电脑装的是Windows系统就直接选择第一项。 下载好之后,一直点击下一步即可,傻瓜式的安装方式,不再赘述。 安装完成之后,WIN + R 打开运行窗口,输入cmd打开命令行窗口,输入node -v命令行。 如果像上图所示显示出Node.js的版本号,则说明已安装成功。 三、使用Node.js搭建本地服务器 1. 新建Node.js项目 新建一个项目文件(此处我命名为node),然后在其中新建一个名为app.js的文件,目录结构如下: 再将命令行中的当前所在目录切换至该项目所在目录(假设该项目建在E盘根目录下)。 2. 编写代码 在app.js编写以下代码: //请求(require)Node.js 自带的 http 模块 var http = require('http'); http.createServer(function (request, response) { // 发送 HTTP 头部 // HTTP 状态值: 200 : OK // 内容类型: text/plain response.writeHead(200, {'Content-Type': 'text/plain'}); // 发送响应数据 "Hello World",最终会在浏览器中显示 response.end('Hello World\n'); }).listen(8888); // "8888" 为端口号 // 终端打印如下信息 console.log('Server running at http://127.0.0.1:8888/'); 3. 运行代码 在终端命令行中输入命令: node app.js: 4. 查看运行结果 经历以上三步,本地服务器已经搭建成功,这时候只需要打开浏览器,在地址栏输入:http://127.0.0.1:8888/ ,回车就可以访问刚创建的本地站点了。若想要关闭站点服务,可以在终端命令行中按下Ctrl + C或者直接关闭终端窗口即可。
一、如何禁止微信h5页面默认下拉,并且同时页面局部可滚动? 众所周知,微信浏览器默认是可以上拉和下拉的,就像下面这样: 微信浏览器下拉 然而,有时候我们需要禁止它,那该怎么做呢? 1. 禁止页面的touchmove事件 document.addEventListener('touchmove',function(e){ e.preventDefault(); }); 该方法简洁明了,但是问题来了,浏览器默认下拉是禁掉了,可页面却不能滑动了。因此,该方法只适用于在单屏内能够显示全内容的页面。 2. 使用iScroll.js插件 该方法才是真正要提到的,既可以禁止浏览器默认下拉,也能同时让页面局部可滚动。 使用iScroll.js插件时需要注意一下几点: ① 调用该插件的对象元素只能存在一个子元素,也就是说如果在滚动区域中存在多个子元素,需要使用一个元素来包裹它们。 ② 调用该插件的对象元素样式中最好不要设置外边距,否则可能出现宽高计算不准确的情况。 ③ 使用该插件的默认状态下,滚动区域内的按钮、链接等一切可点击元素均失效,如需恢复正常点击,可以配置options.click为true。 iScroll.js插件的具体用法可以参考:http://wiki.jikexueyuan.com/project/iscroll-5/。 二、Less中写border-radius时用到 “/” 时该如何处理? 不知道大家现在写CSS是怎么写呢?是直接写原生的CSS,还是使用像Sass或Less这样的预编译语言呢? 如果你是习惯使用Less,那么遇到border-radius中的 “/” 该如何处理呢? 大家可能都知道border-radius有很多种写法,不知道的话可以看《你不知道的CSS3圆角》这篇文章,其中有比较详细的介绍。然而,如果是在Less中直接写 “/” 可不行,因为它会被认为是个除号而直接参与运算了。这时的解决方法就是使用 e("/") 来替代,比如像下面这样: //Less代码 .box{ border-radius: 5px e("/") 10px;} //编译后的CSS代码 .box{ border-radius: 5px / 10px;} 三、Less中如何写循环? Less中的循环其实就像是其他编程语言中通常意义上的递归调用,用法如下: .loop(@n, @i:1) when (@i <= @n){ // 此处仅为函数定义 .loop(@n,@i+1); } .loop(4); // 此处为函数调用,代表循环4次 其中loop是方法名,可以由自己喜好定义,i 是循环因子,n 是循环次数。比如想要给列表元素有规律地依次设置不同大小,可以这样写: //Less代码 .loop(@n, @i:1) when (@i <= @n){ // 此处仅为函数定义 li:nth-child(@{i}){ // 注意此处需用 {} 将 i 括起 width: @i*100px; height: @i*100px; } .loop(@n,@i+1); } .loop(4); //HTML代码 <ul> <li></li> <li></li> <li></li> <li></li> </ul> 这时四个 li 元素的宽高分别为 100、200、300和400。 四、如何将一个数组中的元素全部赋值给另外一个数组? 你可能会想,这还不简单,将 a 数组直接赋值给 b 数组不就行了吗? 就像下面这样: var a = [1,2,3]; var b = a; 实际上通过这种方式赋值只不过是将 a 数组所在地址赋值给了 b,如果 b 中元素改变其实也就改变了 a 元素,那么该如何保证赋值操作之后改变 b 而不会影响 a 呢? 其实这里就涉及到的深拷贝和浅拷贝的区别了。 简单来说,浅拷贝其实就是简单地对地址的赋值,上面例子就是浅拷贝。 而这里我们要说的是数组的深拷贝,也就是开辟一个新的存储地址,然后将 a 数组中的所有元素存入该新地址中,最后将 b 指向该地址。 具体实现其实非常简单: b = [].concat(a); 我们知道,JS数组方法中的concat()是用于合并两个数组的,返回的是一个新的数组。有关concat()的具体用法,可以查看我之前写的一篇文章:《JS数组操作之增删改查》。 以上是我在工作当中所遇到的一些问题总结,在此与大家共勉!
上周由于国庆小长假的缘故未能及时更新文章,在此说声抱歉,之后将继续周更。 好久没有写有关微信小程序的文章了,今天继续讲讲小程序的一些容易踩到的“坑”。 一、图片上传须使用wx.uploadFile(),而不是使用wx.request() 微信小程序专门提供了一个用于上传文件的API,那就是wx.uploadFile()。如果想要上传图片,同样得用该方法,并且一般情况下都是要与wx.chooseImage()结合使用。通过wx.chooseImage()可以选择本地图片或者直接拍照而返回一个临时的图片路径,再将这个临时路径传入wx.uploadFile()中后通过第三方服务器返回线上路径,这样之后我们才能随时获取到这张图片。 当然,有时候我们也可能在提交表单的时候需要提交图片,这时候我们才要用到wx.request(),提交的图片路径就是通过wx.uploadFile()返回的线上路径,而不是通过wx.chooseImage()返回的临时路径。 这两个方法的具体用法可以直接参考开发文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/network-file.html#wxuploadfileobject。 二、微信小程序的加载动画家族 1. 下拉刷新动画 ① onPullDownRefresh() 在 Page 中定义 onPullDownRefresh 处理函数,监听该页面用户下拉刷新事件,需要用户手动下拉才能触发。 ② enablePullDownRefresh 需要在config(文件后缀为.json)的window选项中设置enablePullDownRefresh为true后onPullDownRefresh()才有效。 ③ wx.startPullDownRefresh() 开始下拉刷新,调用后触发下拉刷新动画,效果与用户手动下拉刷新一致,使用该方法无需用户手动下拉也能触发下拉刷新动画。 ④ wx.stopPullDownRefresh() 当处理完数据刷新后,wx.stopPullDownRefresh可以停止当前页面的下拉刷新动画。 2. 导航条加载动画 ① wx.showNavigationBarLoading() 在当前页面显示导航条加载动画。 ② wx.hideNavigationBarLoading() 隐藏导航条加载动画。 3. 提示框加载动画 ① wx.showLoading() 显示 loading 提示框, 需主动调用wx.hideLoading()才能关闭提示框。 ② wx.hideLoading() 隐藏 loading 提示框。 三、page.json只能设置 app.json 中的 window 配置项的内容,并且不能写window这个键 这个“坑”看起来虽小,但是一旦你一不小心踩到了,很可能很难发现问题所在,因为就算你在page.json中写法与app.json一致,也就是说把window这个键也写进去了,小程序并不会报错,只是不会出现你想要的效果。 比如,你只想在某个页面实现下拉刷新效果,那么你在该页面的 config 中可以这样配置: { "navigationBarTitleText": "下拉刷新动画", "enablePullDownRefresh": true } 但是,如果你一不小心在前面加了个"window":,那么问题就来了,页面这时无法实现下拉刷新,然后你说我明明配置了"enablePullDownRefresh": true啊,接着就很有可能在这个小问题上纠结很久,所以需要谨记:page.json中不能写window这个键。 四、微信小程序中含有Imoji图片的用户昵称存储问题如何解决? 这里涉及后端数据库的问题,有时候提交表单时需要获取用户昵称并提交到数据库,但是有些用户昵称中会带有Imoji图片,这时直接保存到数据库可能会出现无法识别的问题,解决方法是将数据库字段格式改成utf8mb4格式就好。 相关推荐: 微信小程序开发之路(一)微信小程序开发之路(二)微信小程序开发之路(三)
一、如何解决Canvas画布在移动端显示模糊的问题? Canvas画布在PC端显示正常,但是放在移动端却发现整个画布都有点模糊,其实这里有个比较简单的解决方法: 先给canvas标签的width和height这两个属性值乘以2,再设置其样式中的width和height的实际大小,最后注意须将JS中与Canvas相关的数值均乘以2。 举个例子: <!--PC端--> <canvas id="canvas" width="400" height="200"></canvas> <script> window.onload = function(){ var canvas = document.getElementById("canvas"); var context = canvas.getContext('2d'); context.font = 'normal 40px "Microsoft Yahei"'; context.textBaseline = 'top'; context.fillText('Hello World!',0,0); }; </script> <!--移动端--> <canvas id="canvas" width="800" height="400" style="width: 400px; height: 200px;"></canvas> <script> window.onload = function(){ var canvas = document.getElementById("canvas"); var context = canvas.getContext('2d'); context.font = 'normal 80px "Microsoft Yahei"'; //这里的字体大小放大了两倍 context.textBaseline = 'top'; context.fillText('Hello World!',0,0); }; </script> 通过以上两个端的代码对比应该就很清楚了,当然,设置Canvas的width和height属性也可以通过JS动态添加: canvas.width = 800; canvas.height = 400; 二、forEach()中不能通过给回调函数参数item赋值的方式来改变数组。 在之前我写的简述forEach()、map()、every()、some()和filter()的用法一文当中提到过forEach()的用法,并且知道array[index] === item,虽然如此,但我们却并不能将这两者完全同等看待。比如说我想要将一个数组中的每一项乘以2,可以这样写: var arr = [1,2,3]; arr.forEach(function(item,index,array){ array[index] *= 2; }); console.log(arr); // [2, 4, 6] 但是我们却不能将例子中的array[index]直接替换成item,这是必须要注意的一点。 三、iOS系统和某些移动端不兼容background-attachment:fixed,该如何解决? 对于这样的CSS兼容性问题,还是直接找个替代方案吧,这里你可使用元素的position:fixed来替代。如果你不想新增一个空的标签元素,也可以使用伪元素::before或::after替代,在伪元素中设置背景图片后将其固定定位,也能达到与background-attachment:fixed相同的效果。 四、在iOS系统下的微信浏览器中,使用<audio>元素无法自动播放音乐,该如何解决? 如果是在PC端,我们完全可以仅仅使用<audio>就能实现音乐自动播放功能,就像下面这样: <audio id="music" src="media/music.mp3" autoplay></audio> 是的,使用的是autoplay属性,轻松就能实现。然而在移动端,为了考虑兼容性,我们得这么写: <audio id="music" src="media/music.mp3"></audio> <script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> var music = document.getElementById("music"); music.play(); document.addEventListener("WeixinJSBridgeReady", function () { // 此处是对微信浏览器下的兼容处理 music.play(); }, false); 事实上,在iOS系统中不仅仅只是微信浏览器下存在这样的问题,在其他浏览器下同样存在,而这里只是解决了微信浏览器下的问题,其他浏览器暂时还未找到合适的方法,如有小伙伴找到相应解决方案,欢迎补充~ 以上是我在工作当中所遇到的一些问题总结,在此与大家共勉!
Canvas,中文译为“画布”,HTML5中新增了<canvas>元素,可以结合JavaScript动态地在画布中绘制图形。 今天,我们不讲Canvas的图形绘制,而是讲如何对图片进行处理。 大概流程非常简单,主要分为以下三个步骤: Canvas图片处理 是的,就像把大象装进冰箱一样简单,哈哈。 一、主要API 整个流程中所用到的主要Canvas API有: 绘制图像: drawImage() 获取图像数据: getImageData() 重写图像数据: putImageData() 导出图像: toDataURL() 1. drawImage() 顾名思义,该方法就是用于将图像绘制于Canvas画布当中,具体用法有三种: ① 在画布上定位图像:context.drawImage(img,x,y) ② 在画布上定位图像,并规定图像的宽度和高度:context.drawImage(img,x,y,width,height) ③ 剪切图像,并在画布上定位被剪切的部分:context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height) 以上参数值描述如下表: 参数 描述 img 规定要使用的图像、画布或视频。 sx 可选。开始剪切的 x 坐标位置。 sy 可选。开始剪切的 y 坐标位置。 swidth 可选。被剪切图像的宽度。 sheight 可选。被剪切图像的高度。 x 在画布上放置图像的 x 坐标位置。 y 在画布上放置图像的 y 坐标位置。 width 可选。要使用的图像的宽度。(伸展或缩小图像) height 可选。要使用的图像的高度。(伸展或缩小图像) 2. getImageData() 该方法用于从Canvas画布中获取图像数据,具体用法如下: 获取画布指定矩形范围内的像素数据:var ImageData = context.getImageData(x,y,width,height) 以上参数值描述如下表: 参数 描述 x 开始复制的左上角位置的 x 坐标。 y 开始复制的左上角位置的 y 坐标。 width 将要复制的矩形区域的宽度。 height 将要复制的矩形区域的高度。 该方法会返回一个ImageData对象,该对象有三个属性分别为:width、height和data,而我们最主要用到的就是这个data数组,因为它保存着图像中每一个像素的数据。有了这些数据之后我们便可以对它们进行处理,最后再将其重写至Canvas画布中,这样就实现了对图片的处理转换。对于该data数组具体用法,我们可以在后面的实例中看到。 3. putImageData() 该方法很简单,就是用于将图像数据重写至Canvas画布中,具体用法如下: context.putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight) 以上参数值描述如下表: 参数 描述 imgData 规定要放回画布的 ImageData 对象。 x ImageData 对象左上角的 x 坐标,以像素计。 y ImageData 对象左上角的 y 坐标,以像素计。 dirtyX 可选。水平值(x),以像素计,在画布上放置图像的位置。 dirtyY 可选。水平值(y),以像素计,在画布上放置图像的位置。 dirtyWidth 可选。在画布上绘制图像所使用的宽度。 dirtyHeight 可选。在画布上绘制图像所使用的高度。 4. toDataURL() 这个方法与以上三种方法不同,它是Canvas对象的方法,该方法返回的是一个包含data URI的字符串,该字符串可直接作为图片路径地址填入<img>标签的src属性当中,具体用法如下: var dataURL = canvas.toDataURL(type, encoderOptions); 以上参数值描述如下表: 参数 描述 type 可选。图片格式,默认为 image/png。 encoderOptions 可选。在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。 二、图片处理实例 本实例将通过代码简单介绍如何把彩色图片处理成黑白图片。 <!--HTML--> <canvas id="canvas" width="600" height="600"></canvas> <input id="handle" type="button" value="处理图片" /> <input id="create" type="button" value="生成图片" /> <div id="result"></div> //JavaScript window.onload = function(){ var canvas = document.getElementById("canvas"), //获取Canvas画布对象 context = canvas.getContext('2d'); //获取2D上下文对象,大多数Canvas API均为此对象方法 var image = new Image(); //定义一个图片对象 image.src = 'imgs/img.jpg'; image.onload = function(){ //此处必须注意!后面所有操作均需在图片加载成功后执行,否则图片将处理无效 context.drawImage(image,0,0); //将图片从Canvas画布的左上角(0,0)位置开始绘制,大小默认为图片实际大小 var handle = document.getElementById("handle"); var create = document.getElementById("create"); handle.onclick = function(){ // 单击“处理图片”按钮,处理图片 var imgData = context.getImageData(0,0,canvas.width,canvas.height); //获取图片数据对象 var data = imgData.data; //获取图片数据数组,该数组中每个像素用4个元素来保存,分别表示红、绿、蓝和透明度值 var average = 0; for (var i = 0; i < data.length; i+=4) { average = Math.floor((data[i]+data[i+1]+data[i+2])/3); //将红、绿、蓝色值求平均值后得到灰度值 data[i] = data[i+1] = data[i+2] = average; 将每个像素点的色值重写 } imgData.data = data; context.putImageData(imgData,0,0); //将处理后的图像数据重写至Canvas画布,此时画布中图像变为黑白色 }; create.onclick = function(){ // 单击“生成图片”按钮,导出图片 var imgSrc = canvas.toDataURL(); //获取图片的DataURL var newImg = new Image(); var result = document.getElementById("result"); newImg.src = imgSrc; //将图片路径赋值给src result.innerHTML = ''; result.appendChild(newImg); }; }; }; 可能上面代码写得不是很好,看起来也不是那么好理解,最好自己能够亲自写一写,这样对于自己理解会更加深刻一些。
一、直接获取元素样式属性值 运用之前在JS如何获取元素样式?这篇文章中提到的三种获取元素样式方法便可获取元素大小和位置,但前提是该元素得事先定义了相应样式。例如: <div id="container" style="position: relative; margin: 10px; padding: 10px; border: 5px solid #f00;"> <div id="box" style="width: 100px; height: 100px; margin: 10px; padding: 10px; border: 5px solid #f00;"></div> </div> var box = document.getElementById("box"); console.log(box.style.width); //100px 以上是简单地获取了元素的宽度,返回的是一个字符串,若要转化成数字,可使用parseInt()方法。 console.log(parseInt(box.style.width)); //100 二、offsetWidth和offsetHeight、offsetLeft和offsetTop 1. offsetWidth和offsetHeight 用于获取元素在屏幕上占用的所有可见的空间大小,具体包括以下几个方面: 内容区域+内边距+边框 拿offsetWidth举个例子(offsetHeight同理): console.log(box.offsetWidth); //130 console.log(typeof box.offsetWidth); //'number' 示例中具体计算是这样的: box.offsetWidth = 100(内容区域宽度) + 20(左右内边距) + 10(左右边框) = 130 从示例中也可以看出,使用offsetWidth获取到的元素宽度是一个数字而不是一个带有单位的字符串,这也是与直接获取样式之间存在的重要区别之一。 2. offsetLeft和offsetTop 用于获取元素的外边框至距离最近的已定位祖先元素的内边框之间的距离,例如: console.log(box.offsetLeft); // 20 由于box的父级是container,且具有position: relative这样的定位样式,所以这里的offsetLeft具体计算是这样的: box.offsetLeft = 10(box左外边距) + 10(container左内边距) = 20 三、clientWidth和clientHeight、clientLeft和clientTop 1. clientWidth和clientHeight 用于获取元素内容及其内边距所占据的大小空间,简单来说就是: 内容区域+内边距(注意不包含滚动条,若有则需减去该部分大小) 拿clientWidth举个例子: console.log(box.clientWidth); //120 这里的计算很简单: box.clientWidth = 100(内容区域宽度) + 20(左右内边距) = 120 但是如果该元素存在滚动条,那么情况就不一样了,比如给box样式中加上overflow: scroll;之后: console.log(box.clientWidth); //103 这是因为,PC端浏览器滚动条默认大小为17px,所以这时: box.clientWidth = 100(内容区域宽度) + 20(左右内边距) - 17(浏览器纵向滚动条宽度) = 103 2. clientLeft和clientTop 这两个属性其实获取的就是元素的边框宽度,比如: console.log(box.clientLeft); //5 这里获取的就是box左边框的宽度。 四、scrollWidth和scrollHeight、scrollLeft和scrollTop 1. scrollWidth和scrollHeight 用于获取包含滚动内容的元素的大小,事实上通过该属性获取的元素大小值与clientWidth和clientHeight获取的值是一样的。例如: console.log(box.scrollWidth); //120 加上滚动条之后: console.log(box.scrollWidth); //103 2. scrollLeft和scrollTop 用于获取元素当前滚动位置,当然也可以设置元素的滚动位置。其中,scrollLeft可获取隐藏在内容区域左侧的像素数,scrollLeft可获取隐藏在内容区域上方的像素数。 五、getBoundingClientRect() 该方法非常方便实用,可直接获取元素与浏览器视口之间的距离。 使用该方法会返回一个矩形对象,包含left、top、right和bottom四个属性,这些属性给出了元素在页面中相对于视口的位置。具体用法很简单,我们来看下面的例子: console.log(box.getBoundingClientRect().left); // 与视口左侧的距离 console.log(box.getBoundingClientRect().top); // 与视口顶部的距离 console.log(box.getBoundingClientRect().right); // 与视口右侧的距离 console.log(box.getBoundingClientRect().bottom); // 与视口底部的距离 通过以上获取到的元素方位,我们也可以计算出该元素的大小。 console.log(box.getBoundingClientRect().right - box.getBoundingClientRect().left); //130 可以看出,该大小值与通过offsetWidth属性获取的值是一样的。 结束语:本文所讲内容都是非常实用的,可能经常都会用到,另外注意其中除了style、scrollLeft和scrollTop是可读可写之外,其他均是只读的。
上篇文章讲到如何设置元素样式,本文将继续给大家分享如何获取元素样式。 一、style,只获取标签上定义的行内样式 在这里讲的style用法包括三个:style、style.cssText和style.getPropertyValue(),直接看个例子吧: /*CSS*/ #box{ width: 200px; height: 200px; background-color: #0f0;} <!--HTML--> <div id="box" style="width: 100px; background-color: #f00;"></div> //JavaScript var box = document.getElementById("box"); console.log(box.style.cssText); // "width: 100px; background-color: rgb(255, 0, 0);" console.log(box.style.width); // "100px" console.log(box.style.height); // "" console.log(box.style.getPropertyValue('background-color')); // "rgb(255, 0, 0)" 通过上面例子我们可以看出,通过这种方式只能获取行内样式,并不能获取到CSS样式表中的样式。 二、cssRules,只获取CSS样式表中定义的样式 接着上面的例子: //JavaScript var sheet = document.styleSheets[0]; // 获取页面中第一个样式表 var rules = sheet.cssRules; // 获取页面中第一个样式表中定义的所有规则,rules[0]即代表第一条规则 console.log(rules[0].style.cssText); // "width: 200px; height: 200px; background-color: rgb(0, 255, 0);" console.log(rules[0].style.width); // "200px" console.log(rules[0].style.height); // "200px" console.log(rules[0].style.getPropertyValue('background-color')); // "rgb(0, 255, 0)" 可以看出,用法其实与上面类似,只不过是主体变为rules[0]而不是box,所以只能获取到样式表中的样式,而并不能获取到行内样式。 三、getComputedStyle(),获取当前元素的计算样式 以上两种方式,都具有太强的针对性,不够灵活,因为获取到的样式可能并不是当前元素最终表现出来的样式。因此,如果想要获取所有样式表层叠而来的当前元素的样式,我们就要用到getComputedStyle()方法。 依然继续前面的例子: console.log(getComputedStyle(box).cssText); // 注意不仅仅只打印现有样式简单的叠加覆盖结果,而是还会有很多其他样式 console.log(getComputedStyle(box).width); // "100px" console.log(getComputedStyle(box).height); // "200px" console.log(getComputedStyle(box).getPropertyValue('background-color')); // "rgb(255, 0, 0)" 很明显,用法还是和style类似,但是通常情况下使用这种方式获取到的样式才是我们真正所需要的。 兼容性:在IE8下,getPropertyValue()、cssRules和getComputedStyle()统统都不兼容,可以分别使用style.[属性名]、rules和currentStyle的方式替代,具体用法本文将不再说明,在此也希望其他开发者放弃兼容IE8及更早版本,如今2017都快接近尾声,微软自己都早已放弃,我们何必继续再惯着那部分少量用户而折磨自己呢?
一看到这个标题,大家可能首先想到的就是使用“[元素].style.[CSS属性名] = [属性值]”这样的套路去设置元素样式,但实际上,我们其实还有其他方式可以选择。 接下来,我将详细介绍三种设置元素样式的方式。 一、style 这个其实就是我们所熟知的方式,举个例子~~ <div id="box"></div> var box = document.getElementById("box"); box.style.width = '100px'; box.style.height = '100px'; box.style.backgroundColor = "#f00"; 显示效果: 这种方式看似简单粗暴,但写法过于繁冗,尤其是在需要添加很多样式时尤为突出。并且需要注意的是,对于使用短划线的CSS属性名,必须将其转换成驼峰大小写形式。(如示例中的backgroundColor) 二、style.cssText 这种方式相对于上面方法更为简洁,更像是直接在元素上写CSS: [元素].style.cssText = [CSS样式]; 例如: box.style.cssText = 'width: 200px; height: 200px; border: 1px solid #f00;'; 确实,写法上很方便。 但是,缺点是后面同样通过这种方式添加的样式会覆盖之前通过style特性指定的样式。 同样还是上面的例子,只不过是将两段JS写在一块: var box = document.getElementById("box"); box.style.width = '100px'; box.style.height = '100px'; box.style.backgroundColor = "#f00"; box.style.cssText = 'width: 200px; height: 200px; border: 1px solid #f00;'; 如果按照层叠样式表的特性,上面定义的红色的背景颜色应该还存在,然而实际上,下面通过style.cssText方式定义的样式会将style(包括style.cssText)方式添加的样式全部重写。所以,这个例子最终表现结果与上面只有一句时的效果是一样的: 最后注意下兼容性,IE8及更早版本均不支持cssText。 三、insertRule() 这个用法相对上面两种方法都较为复杂一些: [sheet].insertRule([CSS样式],指定位置) [sheet]表示某个样式表,它可以通过document.styleSheets来获得。那么,document.styleSheets又是什么呢? 说得简单一点就是应用在文档中的所有样式表,包括通过link标签引入的样式和style标签定义的样式。如果理解上还是有点困难,那么我们还是放个实例吧~~ 首先头部引入样式表: <link rel="stylesheet" type="text/css" href="css/index.css"/> 当然,这个样式表得真实存在,就算里面什么样式都不写也没关系。也可以直接用style标签,内容为空也不要紧。 然后用JS获取这个样式表: var sheet = document.styleSheets[0]; 最后我们就可以给这个样式表中添加样式了: sheet.insertRule('#box{width: 300px; height: 300px; background-color: #0f0;}',0); 如果是在上面所有例子的基础上添加的这段代码,那么显示结果会是这样的: 大小还是200*200大小,背景颜色是绿色,说明通过style.cssText所设置的宽高样式把通过insertRule()设置的样式覆盖了,原因很简单,style(包括style.cssText)方式设置的样式属于行内样式,自然要比通过insertRule()设置的样式优先级更高咯~~ 那么,示例当中insertRule()的第二个参数0又是指的什么呢? 它指的是我们需要添加CSS代码的位置,所以参数0就代表的是该样式表的最开始位置。 例如,我们先给样式表中手动添加一段样式(以下例子与上述例子无关): <style> #box{ width: 100px; height: 100px;} </style> var sheet = document.styleSheets[0]; sheet.insertRule('#box{width: 300px; height: 300px; background-color: #0f0;}',0); 以上执行结果就是,宽高100*100的绿色盒子: 如果将insertRule()中的第二个参数改为1,那么通过JS添加的这段CSS代码相当于添加到了#box{ width: 100px; height: 100px;}的后面,类似下面这样: <style> #box{ width: 100px; height: 100px;} #box{ width: 300px; height: 300px; background-color: #0f0;} </style> 显示结果: 同样需要注意的是,insertRule()不兼容IE8及更早版本,但可以使用addRule()替代,语法稍微有点不同,上面例子这样写: sheet.addRule('#box','width: 300px; height: 300px; background-color: #0f0;',0); 第一个参数代表元素,第二个参数代表CSS样式,第三个参数代表插入位置,前两个参数必选,最后一个可选,不填则默认为0。 结束语:浏览器兼容性问题对于前端开发者来说一直是个很头疼的问题,很多问题也是主要集中在IE上,只希望可恶的IE早日退出历史舞台吧!
JS提供了很多方便操作数组的方法,本文所要分享的就是如何快速对数组进行增、删、改、查。 一、增 1、push() 可接收任意数量的参数,把它们逐个添加至数组末尾,并返回修改后数组的长度。例如: var arr = []; var len = arr.push(1); console.log(arr); // [1] console.log(len); // 1 len = arr.push(2,3); console.log(arr); // [1,2,3] console.log(len); // 3 2、unshift() 该方法与push()类似,也可接收任意数量的参数,只不过是将参数逐个添加至数组前端而已,同样返回新数组长度。咱们接着上面的例子: var len = arr.unshift(0); console.log(arr); // [0, 1, 2, 3] console.log(len); // 4 len = arr.unshift(-2,-1); console.log(arr); // [-2, -1, 0, 1, 2, 3] console.log(len); // 6 3、concat() 该方法与push()方法有点类似,同样是将元素添加至数组末尾,只不过这个数组已经不是原来的那个数组了,而是其副本,所以concat()操作数组后会返回一个新的数组。具体用法如下: ① 不传参数,返回当前数组副本② 传递一或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中③ 传递非数组参数,这些参数就会被直接添加到结果数组的末尾 继续接着上面的栗子: var arr1 = arr.concat(4,[5,6]); console.log(arr); // [-2, -1, 0, 1, 2, 3] console.log(arr1); // [-2, -1, 0, 1, 2, 3, 4, 5, 6] 例子中一目了然,原数组保持不变,新数组后面添加了4、5、6三个元素。 4、splice() 前面的三个方法都具有很大局限性,因为不是添加到数组前就是数组后,而splice()就不一样了,它非常灵活和强大。灵活是因为它可以添加元素到数组的任意位置,强大是因为它除了可以添加元素之外还具有删除和替换元素的功能(这个后面会陆续讲到)。 splice()可以向数组指定位置添加任意数量的元素,需要传入至少3个参数: 起始位置、0(要删除的元素个数)和要添加的元素。 依然接着上面的例子继续: arr.splice(3,0,0.2,0.4,0.6,0.8); console.log(arr); // [-2, -1, 0, 0.2, 0.4, 0.6, 0.8, 1, 2, 3] 可以看出,splice()与push()和unshift()一样是直接在原数组上修改的。 二、删 1、pop() 与push()方法配合使用可以构成后进先出的栈,该方法可从数组末尾删除最后一项并返回该项。 接着上例: var item = arr.pop(); console.log(item); // 3 console.log(arr); // [-2, -1, 0, 0.2, 0.4, 0.6, 0.8, 1, 2] 2、shift() 与push()方法配合使用可以构成先进先出的队列,该方法可删除数组第一项并返回该项。 继续接着上例: var item = arr.shift(); console.log(item); // -2 console.log(arr); // [-1, 0, 0.2, 0.4, 0.6, 0.8, 1, 2] 3、slice() 该方法同concat()一样是返回一个新数组,不会影响原数组,只不过slice()是用来裁剪数组的,返回裁剪下来的数组,具体用法如下: slice()方法可以接受一或两个参数,即要返回项的起始和结束位置。在只有一个参数的情况下,slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。 咱们还是继续接着上面例子吧~~ var arr2 = arr.slice(2,6); console.log(arr); // [-1, 0, 0.2, 0.4, 0.6, 0.8, 1, 2] console.log(arr2); // [0.2, 0.4, 0.6, 0.8] 4、splice() 好,继续讲这个“万能”的方法。 上面讲到,该方法在添加数组元素的时候需要传入3个以上参数,而其中第2个参数就是用于指定要删除元素的个数的,那时我们传的是数字0。那么,如果单单只需删除元素,我们就只需给splice()传入两个参数,第1个参数用于指定要删除的第一项的位置,第2个参数用于指定要删除元素的个数。 继续上例~~ arr.splice(2,4); console.log(arr); // [-1, 0, 1, 2] 从索引项为2的位置开始删除4个元素,所以结果为 [-1, 0, 1, 2]。 三、改 这个其实最灵活的方式就是直接使用splice()这个强大的方法了,其实通过以上对该方法的了解,我们大致就能知道使用该方法修改数组元素的基本原理。 原理很简单,就是向指定位置插入任意数量的元素,且同时删除任意数量的元素。 依然继续上例~~ arr.splice(2,1,0.5,1,1.5); console.log(arr); // [-1, 0, 0.5, 1, 1.5, 2] 四、查 indexOf()和lastIndexOf() 这两个方法都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中,indexOf()从数组的开头(位置0)开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。 例如: var index = arr.indexOf(0); console.log(index); // 1 index = arr.indexOf(3,0); console.log(index); // -1 当找不到该元素时,返回 -1 ,lastIndexOf()方法同理。
在之前的JS闭包问题(一)文章中大概介绍了一下JS闭包,同时讲了闭包与变量之间的问题,今天我们继续聊闭包,聊聊闭包与this对象之间的问题。 我们知道,this指向当前对象,而在全局环境中,this就等于window对象,举个例子: var name = "The Window"; var object = { name: "My Object", getName: function(){ return this.name; } } alert(this); // [object Window] alert(this.name); // "The Window" alert(object.getName()); // "My Object" 上面例子很好理解,在全局环境中,name == window.name == this.name,而object.getName中的this指向object。 那么遇到闭包又会是什么样的结果呢?我们接着往下看: var name = "The Window"; var object = { name: "My Object", getName: function(){ return function(){ return this.name; } } } alert(object.getName()()); // "The Window" 通常情况下,匿名函数的执行环境具有全局性,this对象指向window,所以上面例子中返回 "The Window"。但是,也存在例外的情况: ① 通过call()或者apply()改变函数的执行环境,这时this会指向其他对象 比如还是上面这个例子,只不过最后改成这样: alert(object.getName().call(object)); // "My Object" 虽然还是执行同样一个匿名函数,但是执行环境却强制改成了object,这时的this就指向了object。 ② 给HTML元素添加事件,这时匿名函数中的this会指向该事件所在元素 例如: <input type="button" id="btn" value="点击我吧" /> var oBtn = document.getElementById("btn"); oBtn.onclick = function(){ alert(this.value); // "点击我吧" } oBtn.addEventListener('click',function(){ alert(this.value); // "点击我吧" }); 可以看出,不管哪种绑定事件方式,匿名函数中的this对象均指向oBtn。 总而言之,this对象是在运行时基于函数的执行环境绑定的。
一、如何在元素自身及其父级宽高不确定的情况下让元素水平和垂直方向上居中? 这个问题最常见的就是让文字在浏览器窗口中水平和垂直方向居中了,因为文字的宽度和高度均不确定,浏览器窗口的宽高我们也不知道,那这个问题该如何解决呢? 1、50%定位+translate居中法 html,body{ height: 100%;} .text{ position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);} 2、Flexbox居中法 html,body{ display: flex; justify-content: center; align-items: center; width: 100%; height: 100%;} /*注意这里得写宽度属性*/ 3、Flexbox+margin居中法 html,body{ display: flex; width: 100%; height: 100%;} /*注意这里得写宽度属性*/ .text{ margin: auto;} 其实,关于CSS中的居中方法在我之前的纯CSS七大居中方法这篇文章中已经列举出来了,只不过符合问题中不定宽高条件的只有以上三种方法。 二、Date()对象时间参数格式问题 这个问题说大不大,说小也不小,怕就怕遇到问题时纠结半天都找不到根结所在。举个例子吧~~ var date = new Date("2017-08-04 08:00"); document.write(date); 这段代码在页面上的输出结果是什么? 正常来说应该输出的是标准时间格式: Fri Aug 04 2017 08:00:00 GMT+0800 然而,经过本人测试发现,IE下全军覆没,IE8及其以下浏览器输出NaN,IE8以上浏览器输出Invalid Date ,IOS系统下的所有浏览器也均输出Invalid Date。 那此类问题该如何解决呢? 很简单,将时间参数改为 2017/08/04 08:00 这样的格式就好啦! 三、IOS系统中动态生成的html元素绑定点击事件失效问题如何解决? 我们先来谈谈如何给动态生成的html元素绑定点击事件。 在jquery早期版本中我们可以使用bind()来实现,然而后面建议我们改用on()来实现同样效果,具体用法如下: $(document).on('click','需要绑定事件的元素',function(){}); 例如: $(document).on('click','#btn',function(){ alert('你点击了这个按钮!'); }); $('body').prepend('<div id="btn">按钮</div>'); 以上方法其实就已经解决了绑定点击事件问题,但是在IOS系统下我们需要注意一个问题,那就是在非a标签的元素中可能仍然会存在点击事件失效的问题,这时的解决方案就是给该元素的CSS中加上cursor: pointer这个属性。 四、IOS系统中overflow: auto滑动不流畅如何解决? 又来一个IOS下的坑! 我们制作手机H5的时候,有时候可能需要模拟页面默认的滚动条,这时我们可以使用overflow: auto就很轻松的解决了这个问题,但是却发现在IOS系统中添加了overflow: auto的元素并不能像默认长页面滑动那么流畅,我们手指停止滑动时,页面并不会随着惯性继续滚动一段距离,而是直接停止。其实该问题解决也很简单,直接在该元素上继续添加一个CSS属性-webkit-overflow-scrolling : touch就好。 五、如何使用纯CSS实现单行和多行文本溢出省略? 1、单行文本溢出省略 .ellipsis{ overflow:hidden; white-space: nowrap; text-overflow:ellipsis;} 2、多行文本溢出省略 /*-webkit-line-clamp属性是用来限制行数的,本例是限制两行*/ .mul-ellipsis{ display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;} 看到-webkit-前缀,你就应该知道该方法只适用于移动端,PC端若要实现同样效果的话,额。。貌似只有在后端限制文字字数效果更好了吧。 六、如何解决元素高度从0变为auto时过渡效果无效的问题? 我们可以使用CSS3中的transition属性来实现过渡效果,但是只能对数值有效而对于像auto这样的属性值是无效的,所以若想要实现元素高度从0逐渐变化为auto的效果,可以使用max-height属性来替代height。 以上是我在工作当中所遇到的一些问题总结,在此与大家共勉!
今天,本文会以尽量精简的文字来介绍Flexbox,代码示例会比以往少很多,因为要全面讲述,估计十篇文章都讲不完…… 一、什么是Flexbox? Flexbox,又叫弹性盒子布局。简单来说,它是一种CSS快速布局方式,相比于传统文档流布局方式,具有简洁、高效和响应式等优点。 二、为什么要使用Flexbox? 1、超简洁语法 就说元素水平垂直居中布局这个经典难题吧! 这个问题其实在我之前的纯CSS七大居中方法这篇文章中已经总结过,其中最后两个方法就是用的Flexbox,尤其是最后一个,就两句代码,相比于传统布局,简单到不好意思~~ 父元素:display: flex; 子元素:margin: auto; 2、响应式布局 比如要将一个盒子分成三等分,可以这样写: <ul> <li>1</li> <li>2</li> <li>3</li> </ul> ul{ display: flex; width: 300px;} ul li{ flex: 1; height: 100px;} 是不是很简洁?而且比直接写width: 33.33%精确多了吧。 三、开始学习Flexbox 1、Flexbox模型 大家在中学阶段都学过直角坐标系,它是由垂直的横轴和纵轴构成,横轴方向向右,纵轴方向向上。而今天所要讲的Flexbox与此极为相似,它也是由垂直的两条轴构成,只不过一条叫做主轴,另一条叫做交叉轴(垂直与主轴),图示如下: Flexbox模型(flex-direction: row) 默认情况下主轴就是横轴,方向水平向右,交叉轴就是纵轴,方向竖直向下。具体用法,我们接着往下看。 2、Flexbox众多属性的作用范围 设置父容器的属性有: display: flex | inline-flex; flex-direction: row | row-reverse | column | column-reverse; flex-wrap: nowrap | wrap | wrap-reverse; flex-flow: <flex-direction> || <flex-wrap>; justify-content: flex-start | flex-end | center | space-between | space-around; align-items: flex-start | flex-end | center | baseline | stretch; align-content: flex-start | flex-end | center | space-between | space-around | stretch; 设置子元素的属性有: order: <integer>; flex-grow: <number>; flex-shrink: <number>; flex-basis: <length> | <percentage> | auto | content; flex: none | <flex-grow> <flex-shrink>? || <flex-basis>; align-self: auto | flex-start | flex-end | center | baseline | stretch; 3、Flexbox众多属性介绍 ① display (适用于父容器) flex: 设置该元素为块级元素 inline-flex: 设置该元素为行内元素 该属性可定义布局模式为Flexbox布局,其中display: flex设置该元素为块级元素,display: inline-flex设置该元素为行内元素,可以分别类比传统布局模式下的display: block与display: inline,只不过设置了display: flex | inline-flex的元素其子元素会按照Flexbox的规则进行布局。如果要使用Flexbox布局,则该属性必写。 ② flex-direction (适用于父容器) row(默认值):主轴方向作为默认的书写模式。即横向从左到右排列(左对齐)。 row-reverse:对齐方式与row相反。 column:交叉轴方向作为默认的书写模式。即纵向从上往下排列(顶对齐)。 column-reverse:对齐方式与column相反。 或许正是因为该属性的存在,Flexbox中的横纵轴才会采用主轴和交叉轴这两个名词来描述,这样才不容易造成其他属性功能描述上的混乱。 ③ flex-wrap (适用于父容器) nowrap(默认值):flex容器为单行。该情况下flex子项可能会溢出容器 wrap:flex容器为多行。该情况下flex子项溢出的部分会被放置到新行,子项内部会发生断行 wrap-reverse:反转 wrap 排列。 该属性控制flex容器是单行或者多行,同时主轴的方向决定了新行堆叠的方向。 ④ flex-flow (适用于父容器) <flex-direction>:定义弹性盒子元素的排列方向。 <flex-wrap>:控制flex容器是单行或者多行。 这个属性就不用多讲了,其实就是②和③两个属性的缩写。 ⑤ justify-content (适用于父容器) flex-start(默认值):弹性盒子元素将向行起始位置对齐。 flex-end:弹性盒子元素将向行结束位置对齐。 center:弹性盒子元素将向行中间位置对齐。 space-between:弹性盒子元素会平均地分布在行里。 space-around:弹性盒子元素会平均地分布在行里,两端保留子元素与子元素之间间距大小的一半。 ⑥ align-items (适用于父容器) flex-start:弹性盒子元素的交叉轴起始位置的边界紧靠住该行的交叉轴起始边界。 flex-end:弹性盒子元素的交叉轴起始位置的边界紧靠住该行的交叉轴结束边界。 center:弹性盒子元素在该行的交叉轴上居中放置。 baseline:弹性盒子元素根据它们的基线对齐。 stretch(默认值):弹性盒子元素沿交叉轴方向拉伸填充整个伸缩容器。 ⑦ align-content (适用于父容器) flex-start:各行向弹性盒容器的起始位置堆叠。 flex-end:各行向弹性盒容器的结束位置堆叠。 center:各行向弹性盒容器的中间位置堆叠。 space-between:各行在弹性盒容器中平均分布。 space-around:各行在弹性盒容器中平均分布,两端保留子元素与子元素之间间距大小的一半。 stretch(默认值):各行将会伸展以占用剩余的空间。 ⑧ order (适用于子元素) <integer>(默认值为0):用整数值来定义排列顺序,数值小的排在前面。可以为负值。 设置或检索弹性盒模型对象的子元素出現的順序。 ⑨ flex-grow (适用于子元素) <number>(默认值为0):用数值来定义扩展比率。不允许负值 根据弹性盒子元素所设置的扩展因子作为比率来分配剩余空间。 ⑩ flex-shrink (适用于子元素) <number>(默认值为1):用数值来定义收缩比率。不允许负值 根据弹性盒子元素所设置的收缩因子作为比率来收缩空间。 ⑪ flex-basis (适用于子元素) <length>:用长度值来定义宽度。不允许负值 <percentage>:用百分比来定义宽度。不允许负值 auto(默认值):无特定宽度值,取决于其它属性值 content:基于内容自动计算宽度 如果所有子元素的基准值之和大于剩余空间,则会根据每项设置的基准值,按比率伸缩剩余空间 ⑫ flex (适用于子元素) none:none关键字的计算值为: 0 0 auto <flex-grow>(默认值为1):用来指定扩展比率 <flex-shrink>(默认值为1):用来指定收缩比率 <flex-basis>(默认值为0%):用来指定伸缩基准值,即在根据伸缩比率计算出剩余空间的分布之前,flex子项长度的起始数值。在flex属性中该值如果被指定为auto,则伸缩基准值的计算值是自身的 <width> 设置,如果自身的宽度没有定义,则长度取决于内容。 设置或检索弹性盒模型对象的子元素如何分配空间,是⑨⑩⑪三者的组合写法。 ⑬ align-self (适用于子元素) auto(默认值):如果'align-self'的值为'auto',则其计算值为元素的父元素的'align-items'值,如果其没有父元素,则计算值为'stretch'。 flex-start:弹性盒子元素的交叉轴起始位置的边界紧靠住该行的交叉轴起始边界。 flex-end:弹性盒子元素的交叉轴起始位置的边界紧靠住该行的交叉轴结束边界。 center:弹性盒子元素在该行的交叉轴上居中放置。 baseline:弹性盒子元素根据它们的基线对齐。 stretch:弹性盒子元素沿交叉轴方向拉伸填充整个伸缩容器。 align-items是用于定义父容器中所有的子元素在交叉轴方向上的对齐方式,而align-self则是定义flex子项单独在交叉轴方向上的对齐方式。 兼容性:Flexbox的兼容性目前还是不容乐观的,IE下兼容到IE11,而且也只是兼容一部分属性。 结束语:Flexbox在其发展历程上也是一路坎坷,在目前最新版本之前还有两个老版本,而本文就不做介绍了,由于Flexbox的易用性,以及目前在移动端的兼容性较为良好,相信其未来发展潜力还是相当大的。
在CSS中,要实现同一种效果可能有很多种方式,就比如今天所要讲的多重边框,有人可能会想,那还不简单?要多少边框直接嵌套多少个带边框的DIV元素不就行了么! 是的,这样确实简单粗暴,但是也会因此产生很多毫无实际意义的元素。 事实上,要实现同样效果,一个元素足矣! 接下来我将给大家分享单元素如何实现多重边框效果~~ 一、双重边框效果 <!--HTML--> <div class="box"></div> /*CSS*/ .box{ width: 200px; height: 200px; border: 10px double #000;} 使用border-style: double就可实现简单的双重边框效果,实现效果如下: border-style: double 实现方法虽然简单,但是缺点也是非常明显: ① 无法精确控制双重边框的粗细及之间的间隔; ② 无法改变双重边框的样式,比如双重虚线边框; ③ 无法实现更多层次的边框效果。 二、双重多样化边框效果 /*CSS*/ .box{ width: 200px; height: 200px; border: 1px solid #000; outline: 1px dashed #f00; outline-offset: 10px;} 对于outline属性,我们平时用得比较少,其代表元素的外轮廓,显示于边框外围,大多数情况似乎都只是用于清除表单控件的默认focus样式:outline: none; 而事实上,outline可以制作出与border属性近乎相同的效果,而且写法上也几乎没有差别,但是这里还是大概讲讲这两者存在的细微差别: ① outline不占据实际空间大小,这一点与box-shadow很像; ② outline不能像border一样拆分成border-left、border-right等属性; ③ outline不能设置圆角。 上面例子中还用到了outline-offset属性,这个属性其实是outline在CSS3中新加的属性,该属性不能合并简写在outline中,用于控制外边框与外轮廓之间的距离。 outline属性 这个实现方法也很简单,而且更加灵活,但是也存在几个缺点: ① outline属性无法设置圆角(火狐下可以设置-moz-outline-radius属性来实现圆角,只可惜其他浏览器下并无此属性),所以圆角双重边框无法实现; ② 同样无法实现更多层次的边框效果。 三、多重多样化边框效果 /*CSS*/ .box{ width: 200px; height: 200px; border: 10px solid #000; border-radius: 10px; box-shadow: 0 0 0 10px #f00, 0 0 0 20px #0f0, 0 0 0 30px #00f;} 这里使用了box-shadow属性来替代outline属性,多重阴影效果叠加可以实现无数层边框效果,与此同时也能使用圆角属性border-radius来实现多重圆角边框效果。 box-shadow属性 此实现方式虽然看起来已经达到了我们最初想要实现的效果,但是该方法存在着一个非常显著的缺点,那就是无法像outline或border一样设置虚线边框,所以使用该方法是无法实现多重虚线边框效果的。 兼容性: border当然兼容性是最好的;其次是outline,可以兼容到IE8,但是outline-offset在IE下全军覆没;最后是box-shadow,可以兼容到IE9。 结束语: 本文所介绍的方法各有各自的优缺点,在实际运用当中可以根据运用场景灵活选择,当然,除了以上所写的三种方法之外,我们还可以结合伪元素来实现多重边框,其实最终实现原理还是本文的这几种方法,关于伪元素,你可以看我之前写的伪元素::before与::after的用法这篇文章。
之前我写过一篇JavaScript原型与原型链的文章,此属于JS中的重难点。 而闭包,是JS中除了原型链之外又一个重点和难点。 一、何为闭包 所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。 以上是比较官方的解释,但是感觉晦涩难懂(反正我是没怎么看懂),按照我的理解其实就是函数,具体点就是在函数内部定义的函数,但是就只是这么一说也太不负责任了,我们还是具体以实例来简单理解闭包吧。 function a(n,m){ var bb = 10; return function(){ // 闭包 console.log(bb); // 10 return n+m; } } var sum = a(1,2)(); console.log(sum); // 3 上面这个例子中,a()函数中返回的这个匿名函数就是闭包,而闭包的一个作用就是能够访问外部包含它的函数内的变量。 二、闭包与变量问题 function createFunction(){ var result = []; for(var i=0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } 表面上看这个函数没有什么问题,应该可以正确地返回一个包含10个函数的数组,而每个函数都会返回自己的索引值。然而实际上并不是这样的,实际上所有闭包的返回结果都是10。 console.log(createFunction()[0]()); // 10 console.log(createFunction()[9]()); // 10 这是因为闭包只能取得包含函数中任何变量的最后一个值,那如果要实现上面返回相应索引值应该怎么修改呢? 三、如何解决问题 ① 使用立即执行的匿名函数 修改后的代码如下: function createFunction() { var result = []; for(var i = 0; i < 10; i++) { result[i] = function(num) { return function() { return num; }; }(i); } return result; } console.log(createFunction()[0]()); // 0 console.log(createFunction()[9]()); // 9 修改后的代码使用了一个立即执行匿名函数,这个匿名函数能够在每个循环中获取相应的变量i并作为其参数传递给num并立即执行函数,那么最终返回的那个num变量自然就是递增的 i。 ② 使用ES6中的let关键字来定义变量 i 修改后的代码如下: function createFunction(){ var result = []; for(let i = 0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } 此方法虽然简单,但是目前浏览器对ES6语法兼容性并不好,所以还是建议使用方法①。 结束语: 本人对于JS闭包理解其实也并不是很透彻,如果有不足之处,还请多多指正,谢谢!
在文章开头,先问大家一个问题: 在Javascript中,如何处理数组中的每一项数据? 有人可能会说,这还不简单,直接一个for循环遍历一下就好了。 是的,确实,这是最常见的做法。 但是,除此之外,ES5还提供了处理数组更加方便的方法,如题。 接下来,我将通过几个简单的实例来具体讲解这几个方法。 一、forEach(),用于遍历数组,无返回值 这里先给出一个数组(以下例子通用): var arr = [1,-2,3,4,-5]; 然后我要做事情的就是,将数组中的每一项翻倍。 arr.forEach(function(item,index,array){ array[index] = item * 2; }); console.log(arr); // [2,-4,6,8,-10] 可以看到,forEach()可以传入一个匿名函数作为参数,而该匿名函数有含有三个参数,其依次代表数组遍历时的当前元素item,数组遍历时的当前元素的索引index,以及正在遍历的数组array。有了这三个参数,可以方便我们做很多事情,比如说示例当中将每一项数组元素翻倍,这时需要用到第一个参数item。但是,仅仅只是将item乘以2可不行,我们还得将其赋值给原来的数组,这时我们就得用到后面两个参数index和array。 根据上述可知,array[index]是全等于item的。 arr.forEach(function(item,index,array){ console.log(array[index] === item); // true }); 二、map(),用于遍历数组,返回处理之后的新数组 var newArr = arr.map(function(item,index,array){ return item * 2; }); console.log(newArr); // [2,-4,6,8,-10] 可以看到,该方法与forEach()的功能类似,只不过map()具有返回值,会返回一个新的数组,这样处理数组后也不会影响到原有数组。 三、every(),用于判断数组中的每一项元素是否都满足条件,返回一个布尔值 var isEvery = arr.every(function(item,index,array){ return item > 0; }); console.log(isEvery); // false 可以看到,示例中是要判断数组arr中的元素是否都为正数,很显然不是,所以该方法最终返回false。 四、some(),用于判断数组中的是否存在满足条件的元素,返回一个布尔值 var isSome = arr.some(function(item,index,array){ return item < 0; }); console.log(isSome); // true 可以看到,该方法与every()类似,示例中是要判断数组arr中是否存在负数元素,很显然存在,所以该方法最终返回true。 五、filter(),用于筛选数组中满足条件的元素,返回一个筛选后的新数组 var minus = arr.filter(function(item,index,array){ return item < 0; }); console.log(minus); // [-2, -5] 可以看到,示例中是要筛选出数组arr中的所有负数,所以该方法最终返回一个筛选后的新数组[-2, -5]。 补充: 以上五大方法除了传递一个匿名函数作为参数之外,还可以传第二个参数,该参数用于指定匿名函数内的this指向,例如: // 只传一个匿名函数 arr.forEach(function(item,index,array){ console.log(this); // window }); // 传两个参数 arr.forEach(function(item,index,array){ console.log(this); // [1, -2, 3, 4, -5] },arr); 兼容性: 由于以上方法均属ES5方法,所以IE8及其以下浏览器均不兼容。 重点总结: ① forEach()无返回值,map()和filter()返回新数组,every()和some()返回布尔值 ② 匿名函数中this指向默认为window,可通过传第二参数来更改之 ③ 五种遍历方法均为ES5方法
示例图片 在CSS3之前,想要实现示例图片这样的一个倒影效果一般只能通过处理图片的方式,而CSS3问世之后,想要实现这样的效果变得非常简单,只需一个CSS3属性就可以轻松实现了。 这就是今天所要提到的box-reflect属性。 咱们先看看W3C给出的box-reflect语法: box-reflect:none | <direction> <offset>? <mask-box-image>? (?代表可缺省,也就是后俩属性可有可无)<direction> = above | below | left | right (分别代表:上 | 下 | 左 | 右)<offset> = <length> | <percentage>(分别代表:固定值 | 百分比)<mask-box-image> = none | <url> | <linear-gradient> | <radial-gradient>| <repeating-linear-gradient> | <repeating-radial-gradient>(分别代表:没有遮罩图片 | 遮罩图片路径 | 线性渐变 | 径向渐变 | 重复线性渐变 | 重复径向渐变)默认值:none box-reflect可给两种属性值,一种是none,为默认值,也就是没有任何倒影效果,另一种才是我们今天所要讲的,它可以同时赋予三个属性值,它们依次代表倒影方向、元素与倒影之间的距离以及加在倒影上的遮罩图像,其中后两个属性值可以缺省,但如果<mask-box-image>存在,则<offset>必填。 说了那么多干巴巴的解释,还是联系一下示例应该更好理解些吧。 我们现在来一步步地实现文章开头示例图片的投影效果: ①只给一个属性值below。 <!--HTML代码--> < img class="img" src="imgs/cat.jpg" /> /*CSS代码*/ .img{ -webkit-box-reflect: below; box-reflect: below;} 实现效果: ②再加个5px的间距。 /*CSS代码*/ .img{ -webkit-box-reflect: below 5px; box-reflect: below 5px;} 实现效果: ③最后加个图片遮罩吧。 /*CSS代码*/ .img{ -webkit-box-reflect: below 5px -webkit-linear-gradient(transparent 60%,rgba(0,0,0,.3)); box-reflect: below 5px linear-gradient(transparent 60%,rgba(0,0,0,.3));} 这里的图片遮罩没有用图片,而是用了CSS3中的linear-gradient属性值,它就是用于线性渐变,具体用法这里就不提了,可以直接看这里(http://www.w3cplus.com/content/css3-gradient ),写得很详细。 实现效果: 这就是文章开头示例图片的最终效果。 接下来,再说说径向渐变创建图片遮罩和直接使用图片遮罩。 径向渐变的话其实跟线性渐变类似,只需要你掌握CSS3中基本的径向渐变方法,给个简单示例吧: /*CSS代码*/ .img{ -webkit-box-reflect: left 0 -webkit-radial-gradient(#000 30%,transparent); box-reflect: left 0 radial-gradient(#000 30%,transparent);} 实现效果: 效果很棒!好像猫咪真的在照镜子一样~~ 那如果直接使用图片遮罩呢?比如说我用这样一张图片作为遮罩: /*CSS代码*/ .img{ -webkit-box-reflect: right 0 url(imgs/mask.png); box-reflect: right 0 url(imgs/mask.png);} 实现效果: mask.png 可以看出,遮罩图片会完全拉伸填充倒影图片,并且效果是完全透明部分不会显示出来(其实CSS3渐变图片遮罩原理也跟这个是一样的)。 需要注意的是,以上讲的所有效果不仅仅可以用在图片上,用在其他元素上也是完全可以的,比如说文字。 兼容性: box-reflect虽然看起来效果很不错,但是遗憾的是,目前只有webkit内核浏览器兼容,不过移动端基本已经没有任何问题啦~~
在我之前写的使用hash制作单页Web应用一文中,讲到使用Javascript中的location.hash来制作单页Web应用,看到那么长一段JS只是为了实现一个简单的Tab切换,实在是有点大材小用了。而今天所要分享的,同样是利用hash来实现切换,但是不同的是,这次不写任何JS,而是使用纯CSS来实现相同的效果。 这次我们主要要用到一个CSS伪类选择器:target,它可以给id为当前页面hash值的元素加上相应样式,以下是样例关键代码: <!--HTML关键代码--> <nav class="tab"> <ul> <li><a href="#page1">页面一</a></li> <li><a href="#page2">页面二</a></li> <li><a href="#page3">页面三</a></li> </ul> </nav> <div class="tab-cont"> <ul> <li id="page1">页面一</li> <li id="page2">页面二</li> <li id="page3">页面三</li> </ul> </div> /*CSS关键代码*/ .tab-cont ul li{ opacity: 0;} .tab-cont ul li:target{ opacity: 1;} .tab-cont ul li:nth-child(1){ opacity: 1; } 当点击class属性值为tab的元素中的第二个a标签时, 页面url的hash值变为#page2,所以此时id为page2的li元素就会加上li:target中的样式,这样page2就显示出来了,而其他的li会回到常态的opacity: 0样式,这样就实现了简单的Tab切换。 兼容性: :target伪类选择器的兼容性还是比较好的,可以兼容目前主流浏览器,不兼容IE8及以下版本。
浏览器默认的一些表单样式都不太好看,就比如说在Chrome浏览器下,默认单选框长下面这样: Chrome下默认单选框 灰不溜秋的,什么鬼,其实我想要下面这样的: Chrome下美化单选框 这样看起来就很不错,还加了动画效果,可是默认的单选框只能改变其大小,像背景颜色啥的并不能改变,那该怎么办呢? 没关系,既然不能改变其本身,那咱就自己造一个出来。 我们可以用个简单的span标签(当然你也可以用其他标签)来模拟原生的单选框,大概原理就是,先使用label标签将原生单选框、用于模拟单选框的span标签以及文字包起来,并将原生单选框隐藏,然后再使用:checked伪类和+选择器来给span标签加选中样式。上面GIF图中美化单选框的具体代码如下: <!--HTML代码--> <div class="form-group"> <p>性别:</p> <label><input name="sex" type="radio" checked /><span></span>男</label> <label><input name="sex" type="radio" /><span></span>女</label> </div> /*CSS代码*/ p{ display: inline-block;} label{ position: relative; padding:0 10px 0 25px; cursor: pointer;} label input{ display: none;} label span{ position: absolute; left: 0; top: 0; bottom: 0; width: 16px; height: 16px; margin: auto; border: 1px solid #ccc; border-radius: 100%;} label span:after{ content: ''; position: absolute; top: 0; bottom: 0; left: 0; right: 0; width: 6px; height: 6px; margin: auto; border-radius: 100%; background: #fff; -webkit-transform: scale(0); transform: scale(0); -webkit-transition: all .3s; transition: all .3s;} label input:checked + span{ border-color: #1db0fc; background: #1db0fc;} label input:checked + span:after{ -webkit-transform: scale(1); transform: scale(1);} 以上代码可能有几个地方需要解释一下: ① label标签可以将文字和表单控件绑定在一起,当你点击其中的文字时,与之绑定的表单控件将会相应获取焦点(单选框与复选框是选中该选项); ② :checked伪类选择器是用于筛选被选中的单选框或复选框,需要注意的是,该选择器最低只兼容到IE9; ③ +选择器是用于筛选紧邻其后的兄弟元素,比如示例代码中就是筛选被选中的单选框之后紧邻的span元素。既然这里提到了,就再简单地说说与之相似的~选择器,该选择器是用于筛选其后的所有兄弟元素,这两个选择器可能平时用得比较少,但是有时候用处还是挺大的。 好了,以上就是使用纯CSS来模拟实现单选框的美化,当然复选框的美化原理其实是一样的,这里就不再赘述了。
CSS选择器主要分为五大类:元素选择器,关系选择器,属性选择器,伪类选择器和伪元素选择器。平时用的最多的是元素选择器和关系选择器,而本文主要讲讲用得比较少的属性选择器。 首先亮出本文通用的例子: <!--html--> <p name="zhangsan">张三(zhangsan)</p> <p name="liusanjie">刘三姐(liusanjie)</p> <p name="zhang-ming">张明(zhang-ming)</p> <p name="lisan">黎三(lisan)</p> <p name="sandy lisan">李三(sandy lisan)</p> <p>无名氏</p> 接下来分别举例讲讲七大属性选择器。 一、E[att],筛选具有att属性的元素 css代码: p[name]{ color: #f00;} 显示结果: p[name] 可以看出,只要具有name属性的元素都会被筛选出来,而不具有name属性的“无名氏”颜色没有变化。 二、E[att="val"],筛选att属性值为val的元素 css代码: p[name="lisan"]{ color: #f00;} 显示结果: p[name="lisan"] 可以看出,只要name属性值为“lisan”的元素都会被筛选出来,而name属性为“sandy lisan”的元素并不会被筛选。 三、E[att~="val"],属性值为用空格分隔的字词列表,其中一个等于val的元素(包含只有一个值且该值等于val的情况) css代码: p[name~=lisan]{ color: #f00;} 显示结果: p[name~=lisan] 可以看出,除了可以筛选出name属性值为“lisan”的元素,name属性值为“sandy lisan”的元素同样也会被筛选出来。 四、E[att^="val"],筛选att属性值以val开头的元素 css代码: p[name^=zhang]{ color: #f00;} 显示结果: p[name^=zhang] 可以看出,只要是name属性值以“zhang”开头的元素都会被筛选出来。 五、E[att$="val"],筛选att属性值以val结尾的元素 css代码: p[name$=san]{ color: #f00;} 显示结果: p[name$=san] 可以看出,只要是name属性值以“san”结尾的元素都会被筛选出来。 六、E[att*="val"],筛选att属性值中包含val的元素 css代码: p[name*=san]{ color: #f00;} 显示结果: p[name*=san] 可以看出,只要是name属性值中包含“san”的元素都会被筛选出来。 七、E[att|="val"],筛选att属性值以val开头并且其后紧跟着 “-” 的元素 css代码: p[name|=zhang]{ color: #f00;} 显示结果: image.png 可以看出,只要是name属性值以“zhang”开头并且其后紧跟着 “-” 的元素都会被筛选出来,所以只有“张明”颜色变红,而“张三”颜色不变。 兼容性:以上七个属性选择器均兼容主流浏览器,IE兼容到IE7,可以放心使用。
图片来源于网络 人的行为目的永远摆脱不了八个字:追求快乐,逃离痛苦。 你也许知道熬夜晚睡对身体不好,也许一次次地在内心告诫自己要早点休息,但是你依然还是会在不知不觉中“忙”到深夜。 也许你正在看的小说正看到高潮部分。 也许你正在追的电视剧你很想知道下一集主角怎么样了。 也许你正打着游戏玩得正嗨。 …… 你不想放下你正在做的事情,你乐此不疲,直到深夜。 第二天,你因为前一天晚上没有休息好,一整天无精打采,工作没有激情,生活黯淡无光。 于是,你想要改变,你决定,从今天开始,早睡早起。 到了晚上,你依旧开始看小说,追剧,打游戏…… 你心里默默告诉自己,十一点,一定睡觉。 可是,十一点到了,你一边看着还没看完的电视剧,一边瞄了瞄时间,你说,看完这集就睡。 很快,一集电视剧结束了,可故事还没结束,结尾留了个很好的悬念,于是你说干脆再看一集好了,反正一天晚睡又没事,明天早点睡就好了。就这样,早睡早起的决心早已抛之脑后了。 于是,日复一日,年复一年,晚睡恶习还在继续,甚至越来越晚。 时间久了,年纪大了,身体问题开始越来越多,很多问题很可能就是你自己曾经仗着年轻晚睡熬夜而积累下的病根。 可为什么,你明明知道晚睡恶习可能导致严重恶果,可你依然无法改变现状呢?就像你明明知道吸烟有害健康,可你为何还花大量金钱去品尝那个危害你健康的“毒药”呢? 晚睡,拖延,根本原因就是文章开头那八个字,追求快乐,逃离痛苦。 你晚睡,是因为你正在追求你正在享受的快乐,比如说电视剧和游戏等,而你却暂时感受不到现在由于你的晚睡习惯而导致未来的那种持久的痛苦,所以你只管追求当前的快乐,而忽略了未来的痛苦。 换句话说,晚睡在目前给你带来了快乐,却没有让你感受痛苦。 因此,想要解决晚睡拖延症,就应该从根本上改变。解决方法很简单,就是惩罚晚睡,奖励早睡,这就是本文要讲的奖惩机制。 当你还是个孩子的时候,父母或老师总会在你取得好成绩的时候给予一定的奖励,而在你过于贪玩成绩考砸的情况下给予惩罚,这其实就是一种奖惩机制。然而,当你渐渐长大了,很多事情你可以自己做主了,没有了很多外界的约束力,于是你开始放纵自己,骄奢淫逸,晚睡晚起,久而久之形成各种各样的坏习惯。也许有时候你会想着要改变现状,可发现此时早已力不从心。 在这个时候,虽然没有外界约束力,但我们还存在内在的约束力,所谓的自制力就是一种内在的约束力,它也可以使用奖惩机制来对你的行为习惯进行合理约束,但是这个约束力的强度和效果还是完全取决于自己对奖惩机制的执行程度。 接下来将具体介绍如何利用自我奖惩机制彻底征服晚睡拖延症。 第一步,了解自己目前不难做到但是却不愿意去做的事情,比如说锻炼,可以细化到俯卧撑、仰卧起坐、跑步等等。 第二步,了解自己目前最想去做却不难做到的事情,比如说换个新手机,买个时尚包包,去某个景区游玩等等。 第三步,设置睡觉时间节点和具体奖惩规则。 那么以上就形成了最简单的自我奖惩机制,一旦晚上超过设置的时间节点,立刻做出相应惩罚,最佳效果是将惩罚量化,比如超过一分钟,做10个俯卧撑。当然,光有惩罚还不行,最好还得有奖励,比如连续一个月在睡觉时间节点前入睡,则给予第二步中所给出的奖励。相信只要自己能严格执行以上自我奖惩机制,很快你将摆脱晚睡拖延症的困扰,但是一旦你为自己寻找借口,并不愿去实施惩罚,或者提前做完自己最想做的事情,那么该机制将失去它原本存在的意义了。 方法就在这里,实践完全在你自己。 最后总结一下: 大脑的本能:追求快乐,逃离痛苦 实施自我奖惩可以有效摆脱晚睡拖延症的困扰 自我奖惩机制的最佳实施效果是将奖惩内容细化和量化 奖惩实施需立即严格执行,切勿给自己找借口