找到
54
篇与
Web前端
相关的结果
- 第 4 页
-
CSS奇思妙想:动态点亮文本下划线 一、简介 在现代网页中,为用户提供流畅且具有吸引力的交互体验至关重要。本文分享了一种纯CSS实现的动态文本下划线效果。当鼠标悬停在文本上时,无论文本的宽度如何、有多少行、行中有多少字,下划线都会以从左到右、从上到下的顺序,自适应的在文本底部动态、平滑地显示出来。而当鼠标移出时,下划线则按照相反的方向,逐行动态、平滑地隐藏,直到恢复至最初状态。具体效果如下: 案例效果 图片 该动画效果看起来很简单,具体实现也确实不难,难的是实现思路,需要扎实的前端CSS基础和很大的脑洞。该动画效果中利用到了:行内元素的基础特性、元素背景特性、trasition过渡等CSS的基础知识。 该动画可用于资料列表、新闻列表等页面场景中,可以突出显示鼠标所聚焦的文本内容,提升用户的视觉交互体验。 二、具体实现 1、实现思路 ① 看到文本下划线,我们首先会考虑通过text-decoration属性来实现,将文本的该属性默认状态设置为none,然后在hover的时候将其设置为underline,使文本出现下划线。但仔细想想,会发现这个方案并不可取,因为text-decoration是一个离散属性,只能在不同属性值状态之间切换,无法通过trasition过渡实现平滑动画。 ② 排除掉text-decoration属性之后,我们继续思考还有什么样式属性可以实现类似下划线的效果。此时应该想到两个属性:border和background。然后进一步思考,发现border只能控制宽度或者颜色,而且每一行的border只能同时显示,或者同时隐藏,无法实现逐行按序显示/隐藏下划线的效果。排除掉border后,最终确定需要依赖background属性来实现目标效果。 ③ 确定属性之后,首先我们要使用行内元素(或设置display: inline;的其他元素)来承载文本内容。因为行内元素具有一个重要的特性:元素的宽度和高度由其内容决定,当文本内容换行时,从布局角度看,每个换行后的文本片段会被视为单独的内联内容部分,而不是一个整体的块级元素那样有明确的整体宽度和高度概念。这一点特性,在后面通过背景实现下划线效果时将会起到关键作用。 ④ 接下来思考应该如何设置背景属性。目标效果中下划线是纯色的,首先想到的应该是设置背景颜色,但这种想法是错误的。因为设置背景颜色会直接应用到整个行内元素内容区域,而不是对每行的内联文本片段进行单独设置,且无法控制尺寸和位置。但如果给行内元素设置的背景图片,则会基于每行的内联文本片段来进行处理,而非整体内容区域,并且可以通过 background-size 和 background-position 属性进行精确控制。简单来说:我们给换行的行内元素设置背景图片时,其实际效果并非给整个内容块设置背景,而是给每行的内联文本片段设置背景。 ⑤ 经过前面四步的思考,我们已经明确了实现目标效果的关键,接下来只需按部就班设置相关样式即可。首先通过linear-gradient生成一张纯色图片作为元素的背景图片,并设置为不允许重复;其次设置background-size属性,设置初始宽高,使其看起来为一条线;然后通过background-position将背景定位在元素底部,变为下划线。此时查看页面样式,发现每行文本底部的下划线效果已经实现。 ⑥ 最后我们实现平滑动画效果。首先设置background-size属性,将初始宽度设置为0,将背景隐藏;然后在元素hover的时候,将背景宽度恢复为100%,显示背景;再然后借助transition属性,实现平滑的过渡效果,使得背景能逐行动态平滑的显示。 2、具体代码 HTML <div class="title-box"> <span> 有志者,事竟成。破釜沉舟,百二秦关终属楚。苦心人,天不负。卧薪尝胆,三千越甲可吞吴。有志者,事竟成。破釜沉舟,百二秦关终属楚。苦心人,天不负。卧薪尝胆,三千越甲可吞吴。 </span> </div>CSS .title-box { /* 父级盒子设置宽度 限制内部文本宽度 使其换行 */ width: 336px; margin: 50px auto; } .title-box span { color: #333; /* 通过渐变设置图片背景 且不允许重复 */ background: linear-gradient(to right, #333, #333) no-repeat; /* 背景初始宽度为0 高度为2px */ background-size: 0 2px; /* 背景位置在文字底部 */ background-position: left bottom; /* 设置平滑过渡效果 */ transition: background-size 1.5s ease-in-out; cursor: pointer; } /* hover时触发动画 */ .title-box span:hover { /* 背景宽度变为100% 高度不变 */ background-size: 100% 2px; /* 可以通过改变背景位置 改变下划线出现和消失的方向 */ /* background-position: right bottom; */ }三、相似扩展 1、效果1 图片 2、效果2 图片
-
揭开迷雾:全面解析 JS 中的 this 指向问题 一、简介 身为一个使用JavaScript的开发者,我们或多或少在开发中都遇到过因 this 指向不明导致的代码错误。函数中this 关键字的指向是一个复杂但又关键的概念,大多数时候它的指向不是在编写时确定的,而是在代码执行时根据函数的调用方式来决定,简单来说,this 的指向取决于函数的调用者(箭头函数除外)。 本文将全面详细的讲解this的指向规则,让你能够清晰理解并正确使用 this,避免由于错误的指向导致的程序异常,并提升对JavaScript的整体理解。 二、详解 1、 全局作用域 在全局作用域下(非严格模式),也就是最外层的JS脚本中,this指向全局对象(浏览器中为window对象)。 <script> console.log(this === window); // 浏览器中输出 true </script>2、普通函数 在调用一个独立的普通函数时(非严格模式),此时相当于通过全局对象来调用函数,this会进行默认绑定,指向全局对象(浏览器中为window对象)。 重点:普通函数中的 this 指向是动态的,取决于函数的调用方式,会因为调用方式的不同而发生改变。 // 非严格模式下 // 两个普通函数 function showThis() { console.log(this); } const showThis2 = function() { console.log(this); } // 调用普通话函数 showThis(); // 输出 window 对象 showThis2(); // 输出 window 对象 // 相当于通过window来调用函数 因此this指向window window.showThis(); // 输出 window window.showThis2(); // 输出 window如果让一个对象内部的方法指向普通函数,并通过该对象来调用函数,则this指向发生改变,指向调用函数的对象,谁进行调用,this就指向谁。 // 普通函数 function showThis() { console.log(this); } // 创建对象 并让对象的方法指向函数 const obj = { getThis: showThis }; // 通过对象调用函数 obj.getThis(); // 输出 obj3、箭头函数 箭头函数不会创建自己的this,其内部的this会继承函数定义时其外层最近的非箭头函数的 this 指向。如果是在全局作用域内使用箭头函数,则this指向全局对象;如果是在构造函数中使用箭头函数,则this指向new出来的新对象... 重点:箭头函数中的 this 指向是静态的,在函数定义时确定,后面不会因为调用方式的不同而发生改变,也无法被call/apply/bind等方法改变。 // 非严格模式下 // 箭头函数 this指向window const showThis3 = () => { console.log(this); } // 调用箭头函数 showThis3(); // 输出 window 对象如果让一个对象内部的方法指向箭头函数,并通过该对象来调用函数,此时虽然函数的调用者发生变化,但this指向不会改变,依旧指向全局对象。 // 箭头函数 const showThis3 = () => { console.log(this); } // 创建对象 并让对象的方法指向函数 const obj = { getThis: showThis3 }; // 通过对象调用函数 但this指向不会改变 obj.getThis(); // 输出 window4、调用对象的方法 在通过对象直接调用其内部的方法时,方法中的this 会进行隐式绑定,指向当前对象。 const obj = { getThis: function() { // 对象的方法中的this 指向当前对象 console.log(this) } }; // 通过对象调用方法 this指向当前对象 obj.getThis(); // 输出 obj如果先用一个变量指向对象的方法,然后再通过变量来调用对应的方法时,this的指向将不再指向对象,而是指向全局对象(浏览器中为window对象)。因为此时相当于进行独立函数调用,遵守独立函数的默认绑定规则。 <script> const obj = { getThis: function () { console.log(this) } }; // 通过对象调用方法 this指向对象本身 obj.getThis(); // 输出 obj // 使用变量指向对象的方法 但不调用 const fun = obj.getThis; // 通过变量调用方法 此时相当于独立函数调用 所以this指向window fun(); // 输出 window </script>如果使用一个对象的方法指向另一个对象的方法,此时两个对象都可以调用该方法,那么具体的this指向取决于调用者,谁进行调用, this就指向谁。 <script> const obj = { getThis: function () { console.log(this) } }; // 通过obj调用方法 this指向obj本身 obj.getName(); // 输出 obj // 在另一个对象中使用属性指向前面对象的方法 const obj2 = { // 指向obj的方法 get2: obj.getThis } // 通过obj2调用方法 此时this指向obj2 obj2.get2(); // 输出 obj2 </script>还存在一种特殊情况:如果对象的方法使用的是箭头函数,那么方法中的 this 将继承定义对象时所在作用域的this指向,而非对象本身,并且后续不会因为调用者改变而改变this指向。 <script> // 如果对象的方法使用的是箭头函数 那么this将继承定义对象时所在作用域的this指向 // 在全局作用域下定义对象 const obj = { getThis2: () => { // 此时箭头函数中的this 指向全局对象window console.log(this) } } // 虽然是通过obj调用的 但是其this还是会指向全局对象window obj.getThis2(); // 输出 window // 使用变量指向对象的方法 但不调用 const fun = obj.getThis2; // 通过变量调用方法 this依旧指向window fun(); // 输出 window // 在另一个对象中使用属性指向对象的方法 const obj2 = { // 指向obj的方法 get2: obj.getThis2 } // 通过obj2调用方法 此时this依旧指向window obj2.get2(); // 输出 window </script>5、调用构造函数 在使用new关键字调用构造函数时,会创建一个新的对象,此时this 会指向被创建的新对象。内部的普通函数和箭头函数内的 this 也都指向被创建的新对象,但后续普通函数的this指向是会根据调用者不同而发生改变,箭头函数则不会改变。 // 构造函数 function Person(name) { // 构造函数中的this指向通过new创建的新对象 this.name = name; // 普通函数 this.getThis = function () { console.log(this) } // 箭头函数 this.getThis2 = () => { console.log(this) } } // 调用构造函数创建新对象 const person = new Person('Bob'); // 通过新对象调用方法 此时this指向新对象 person.getThis(); // 输出 person person.getThis2(); // 输出 person // 在另一个对象中使用属性指向new出来的新对象的方法 const obj = { getThis: person.getThis, getThis2: person.getThis2 } // 通过obj调用方法 此时this指向会根据函数类型不同而不同 obj.getThis(); // 输出 obj obj.getThis2(); // 输出 person 还存在一种特殊情况,就是Function()构造函数,在使用该构造函数创建新函数时,其内部的this指向并非指向新函数本身,而是根据函数被调用的方式来决定this指向,谁调用this就指向谁。 // 非严格模式 // 在全局作用域下 const func = new Function('console.log(this)'); // 相当于通过window来调用函数 因此this指向window func(); // 输出 window 对象 // 在对象中 const p = { name: 'Alice', get: func } // 通过对象中调用函数 对象内的this指向对象本身 p.get(); // 输出 Person { name: 'Alice', get: [Function] }6、事件处理函数 在事件处理函数中,如果绑定的函数是普通函数,则相当于由DOM元素调用函数,则函数内的this指向触发事件的DOM元素;如果绑定的是箭头函数,则函数内的this会继承绑定事件处理函数所在作用域的this指向。 <button id="btn">Click me</button> <!-- 内联的事件处理器也一样 --> <button onclick="console.log(this)">Click me</button> <script> const btn = document.getElementById('btn'); // 绑定普通函数 相当于dom调用函数 所以this指向button元素 btn.addEventListener('click', function () { console.log('普通函数this--', this); // 输出 button 元素 }); // 绑定箭头函数 继承绑定事件处理函数所在作用域的this 此时为全局作用域 所以this指向window btn.addEventListener('click', () => { console.log('箭头函数this--', this); // 输出 window }); </script>7、回调函数 当把一个函数作为回调函数传递后,其this指向取决于回调函数的调用方式和函数的类型两者: 如果函数类型为普通函数且被传递后是直接独立调用的,此时相当于独立函数调用,函数内部this指向全局对象(浏览器中为window对象)。 如果函数类型为普通函数且被传递后是通过别的对象进行调用的,此时相当于通过对象调用函数,函数内部的this指向函数的调用者。 如果函数类型为箭头函数,则无论被传递后是如何进行调用的,都不会修改函数内部的this指向,箭头函数内部的this指向在函数定义后确定就不会再被改变。 const obj = { // 普通函数 showThis: function () { console.log(this); }, // 箭头函数 showThis2: () => { console.log(this); } } function fun(fn) { // 直接独立调用回调函数 fn(); } function fun2(fn) { // 通过对象调用回调函数 const a = { getThis: fn, } a.getThis(); } // 对象普通函数中的this指向对象本身 obj.showThis(); // 输出 obj // 对象箭头函数中的this继承定义对象时所在作用域的this指向 此时为window obj.showThis2(); // 输出 window // 传入一个普通函数 并且直接调用函数 相当于独立函数调用 所以this指向window fun(obj.showThis); // 输出 window // 传入一个箭头函数 并且直接调用函数 this指向不受影响 依旧为window fun(obj.showThis2); // 输出 window // 传入一个普通函数 并且通过对象调用函数 this被改变 指向其调用者 fun2(obj.showThis); // 输出调用者对象 a // 传入一个箭头函数 并且通过对象调用函数 this指向不受影响 依旧为window fun2(obj.showThis2); // 输出 window数组提供的内置方法中,除了传递回调函数之外,还可以通过第二个参数指定回调函数的this指向。如果未指定this的指向,则取决于函数的调用环境内的this指向。 // 创建对象 const obj = { multiplier: 2 }; // 创建数组 const numbers = [1, 2, 3]; // 使用数组提供的map方法 第一个参数是回调函数 第二个参数是指定回调函数的this指向 const doubled = numbers.map(function (number) { // 回调函数中的this指向obj 所以this.multiplier指向obj.multiplier 值为2 return number * this.multiplier; }, obj); // 最终运算结果为[2, 4, 6] console.log(doubled); // 未指定this指向 回调函数中的this指向window const doubled2 = numbers.map(function (number) { return this; }); // 最终运算结果为[window, window, window] console.log(doubled2);8、call/apply/bind 显式指定 call()、apply()、bind()三个方法都可以显式的指定函数的this指向,只是在一些具体细节上有所不同。例如:call() 和 apply() 方法会在改变函数内部this指向的同时,调用函数执行一次。而bind() 不会调用函数执行,仅改变函数的this指向;在向函数传递参数时,call() 和 bind() 传递的参数都是用逗号隔开的独立参数,而apply()是以数组的形式来传递参数。 // call方法 function greet(text) { console.log(`${text}, ${this.name}`); } const user = { name: 'Charlie' }; // 使函数的this指向user 且会调用执行一次 greet.call(user, 'Hello'); // 输出 'Hello, Charlie' // apply方法 function introduce(age, gender) { console.log(`Name: ${this.name}, Age: ${age}, Gender: ${gender}`); } const user2 = { name: 'Dana' }; // 使函数的this指向user 且会调用执行一次 introduce.apply(user2, [25, 'Female']); // 输出 'Name: Dana, Age: 25, Gender: Female' // bind方法 const module = { x: 42, getX: function () { return this.x; } }; // 此时指向对象的方法 但不调用执行 const retrieveX = module.getX; // 调用执行 此时的 this 指向window 因为相当于独立函数调用 console.log(retrieveX()); // 输出 undefined // 修改this指向 使函数的this指向module 且不会调用执行 const boundGetX = retrieveX.bind(module); // 手动调用执行一次 console.log(boundGetX()); // 输出 42三、课后练习 1、下面代码的执行结果是什么? // 全局作用域 且非严格模式 [1, 2, 3].forEach(function (item) { console.log(this); });2、下面代码的执行结果是什么? // 全局作用域 且非严格模式 const obj = { a: () => { console.log(this); }, b: function () { console.log(this); } }; obj.a(); obj.b();3、下面代码的执行结果是什么? // 全局作用域 且非严格模式 function Person(name) { this.name = name; this.getName = () => { return this.name; }; } const person1 = new Person('Alice'); const person2 = { name: 'Bob' }; person2.getName = person1.getName; console.log(person2.getName());欢迎大家在评论区揭晓答案
-
15 分钟带你感受 CSS :has() 选择器的强大 最近看到了许多关于 :has() 选择器的知识点,在此总结下来。 MDN 对 :has() 选择器 的解释是这样的: CSS 函数式 伪类 :has() 表示一个元素,如果作为参数传递的任何 相对选择器 在锚定到该元素时,至少匹配一个元素。这个伪类通过把 可容错相对选择器列表 作为参数,提供了一种针对引用元素选择父元素或者先前的兄弟元素的方法。下面一起来感受下 :has() 选择器的强大之处吧。 :has() 选择器选择父元素和前面的兄弟元素 邻接兄弟选择器(+)用来选中恰好处于另一个在继承关系上同级的元素旁边的物件。例如,选中所有紧随<p>元素之后的<img>元素: p + img { }通用兄弟关系选择器(~)用来选中一个元素后面的所有兄弟元素。例如,选中<p>元素之后的所有的<img>元素: p ~ img { }css 并没有提供直接选择父元素或者前面的兄弟元素的选择器,但 :has() 可以做到这点。 1、比如选择所有包含 <p>元素的父元素: :has(p) { }2、选择直接后代元素包含 <p>元素的父元素: :has(> p) { }3、选择直接后代元素包含 <p>元素的父级标签名是 div父元素: div:has(> p) { }4、选择 <p>元素的相邻的前一个标签名是 div的兄弟元素: div:has(+ p) { }5、选择 <p>元素的前面所有标签名是 div的兄弟元素: div:has(~ p) { }:has() 选择器中的 且 和 或 在 :has() 选择器中表示 且 和 或 很简单,例如: p:has(.a):has(.b) 表示选择同时包含子元素 a 和 子元素 b 的 元素 p p:has(.a, .b) 表示选择包含子元素 a 或者包含子元素 b 的 元素 p :has() 选择器选择一个范围内的元素 现在有如下元素 <div> <h2>标题开始(选择第一行字体为绿色,最后一行字体为红色)</h2> <p>h2中间第一行</p> <h4>h2中间第二行</h4> <h5>h2中间最后一行</h5> <h2>标题结束</h2> </div>要求选择第一行字体为绿色,最后一行字体为红色。需要注意的是,中间元素可以是任意的。 cc.png图片 使用 :has() 实现上面效果,可以这么做 /* 选择 h2 中间第一行 */ h2+ :has(~ h2) { color: green; } /* 选择 h2 中间最后一行 */ h2~ :has(+ h2) { color: red; }h2 + :has(~ h2) 表示选择紧跟着 h2 的并且后面还有 h2 元素的兄弟元素。也就选择到了 h2 范围内的第一个元素。 h2 ~ :has(+ h2) 表示选择 h2 后面的兄弟元素,并且该兄弟元素的下一个兄弟元素是 h2,也就选择到了 h2 范围内最后一个元素 那如果要选择中间所有元素呢,可以这样做 dd.png图片 /* 选择 hr 中间所有行 */ hr~ :has(~ hr) { color: blue; }:has()选择器的应用 1、CSS :has() 选择器之星级评分 关于星级评分,之前写过一篇文章分享过三种方式使用纯 CSS 实现星级评分。 这里介绍下使用 :has() 选择器 + :not() 选择器 实现星级评分的方式。 星级评分效果包括鼠标滑入和点击,滑入或点击到第几颗星的位置,该位置之前的星高亮,之后的星不高亮或者有高亮的则取消高亮; star.webp图片 html 结构 <div> <input type="radio" name="radio" id="radio1"> <label for="radio1"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"> <path fill="currentColor" d="M283.84 867.84 512 747.776l228.16 119.936a6.4 6.4 0 0 0 9.28-6.72l-43.52-254.08 184.512-179.904a6.4 6.4 0 0 0-3.52-10.88l-255.104-37.12L517.76 147.904a6.4 6.4 0 0 0-11.52 0L392.192 379.072l-255.104 37.12a6.4 6.4 0 0 0-3.52 10.88L318.08 606.976l-43.584 254.08a6.4 6.4 0 0 0 9.28 6.72z"> </path> </svg> </label> <input type="radio" name="radio" id="radio2"> <label for="radio2"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"> <path fill="currentColor" d="M283.84 867.84 512 747.776l228.16 119.936a6.4 6.4 0 0 0 9.28-6.72l-43.52-254.08 184.512-179.904a6.4 6.4 0 0 0-3.52-10.88l-255.104-37.12L517.76 147.904a6.4 6.4 0 0 0-11.52 0L392.192 379.072l-255.104 37.12a6.4 6.4 0 0 0-3.52 10.88L318.08 606.976l-43.584 254.08a6.4 6.4 0 0 0 9.28 6.72z"> </path> </svg> </label> <input type="radio" name="radio" id="radio3"> <label for="radio3"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"> <path fill="currentColor" d="M283.84 867.84 512 747.776l228.16 119.936a6.4 6.4 0 0 0 9.28-6.72l-43.52-254.08 184.512-179.904a6.4 6.4 0 0 0-3.52-10.88l-255.104-37.12L517.76 147.904a6.4 6.4 0 0 0-11.52 0L392.192 379.072l-255.104 37.12a6.4 6.4 0 0 0-3.52 10.88L318.08 606.976l-43.584 254.08a6.4 6.4 0 0 0 9.28 6.72z"> </path> </svg> </label> <input type="radio" name="radio" id="radio4"> <label for="radio4"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"> <path fill="currentColor" d="M283.84 867.84 512 747.776l228.16 119.936a6.4 6.4 0 0 0 9.28-6.72l-43.52-254.08 184.512-179.904a6.4 6.4 0 0 0-3.52-10.88l-255.104-37.12L517.76 147.904a6.4 6.4 0 0 0-11.52 0L392.192 379.072l-255.104 37.12a6.4 6.4 0 0 0-3.52 10.88L318.08 606.976l-43.584 254.08a6.4 6.4 0 0 0 9.28 6.72z"> </path> </svg> </label> <input type="radio" name="radio" id="radio5"> <label for="radio5"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"> <path fill="currentColor" d="M283.84 867.84 512 747.776l228.16 119.936a6.4 6.4 0 0 0 9.28-6.72l-43.52-254.08 184.512-179.904a6.4 6.4 0 0 0-3.52-10.88l-255.104-37.12L517.76 147.904a6.4 6.4 0 0 0-11.52 0L392.192 379.072l-255.104 37.12a6.4 6.4 0 0 0-3.52 10.88L318.08 606.976l-43.584 254.08a6.4 6.4 0 0 0 9.28 6.72z"> </path> </svg> </label> </div>为了使星星有点击效果,利用 radio + label 的方式实现点击效果;label 代表星星。 当点击星星时,高亮当前星星 input:checked+label { color: gold; }当鼠标移入星星时,高亮当前星星,并且该位置之后的星星取消高亮; label:hover { color: gold; &~label { color: #ccc !important; } }让当前位置之前的所有星星也高亮,可以利用 :not ,排除掉当前位置和当前位置之后的星星。 label:not(:hover, :hover ~ *) { color: gold; }并且只有鼠标滑入时添加这些效果。 div:has(label:hover) label:not(:hover, :hover ~ *) { color: gold; }同样,当点击星星时,点亮当前选择的之前所有的星星也如此 div:has(input:checked) label:not(input:checked ~ label) { color: gold; }完整示例 2、CSS :not 和 :has() 模拟 :only-of-type 有下面的 html 结构 <div> <p>第一页</p> <p class="this">第二页</p> <p>第三页</p> <p>第四页</p> </div>要选择类名为 this 的元素,并设置颜色为红色,使用 .this{color:red;} 可以轻松做到。 aa.png图片 如果现在有两个 div 元素块 <div> <p>第一页</p> <p class="this">第二页</p> <p>第三页</p> <p>第四页</p> </div> <div> <p>第一页</p> <p class="this">第二页</p> <p class="this">第三页</p> <p>第四页</p> </div>现要求选择 div 的子元素中只有含有一个类名为 this 的元素(也就是第一个 div 元素块),并且设置其颜色为红色,该怎么做呢? :only-of-type 代表了任意一个元素,这个元素没有其他相同类型的兄弟元素。 但 :only-of-type 判断是否有相同类型的依据是标签名,而不是类名。所以并不能达到想要的效果。 /* 这种写法是无效的,无法判断元素有没有其他相同的类名。 */ .this:only-of-type { color: red; } /* 这种写法是有效的,但判断的是没有相同的 p 的元素,显然无法满足上面的要求,但能匹配下面 ul 中的 p */ p:only-of-type { color: red; }<ul> <li>第一页</li> <li class="this">第二页</li> <li class="this">第三页</li> <p>第四页</p> </ul>而 :has 能做到,要选择前后没有相同类名的元素 ,也就是排除前后的 .this 。 排除前面的 .this /* 表示选择前面没有 .this 的 .this */ .this:not(.this ~) { }排除后面的 .this, /* 表示排除后面有 .this 的 .this */ .this:not(:has(~ .this)) { }两个做并集,也就选择到了唯一的 .this .this:not(:has(~ .this)):not(.this ~ *) { color: red; }bb.png图片 完整示例 3、CSS :has() 选择器之模仿 mac 电脑 dock 栏 利用 :has() 可以选择到前面的兄弟元素的特点,还能做出下面的动画效果 aa.gif图片 当鼠标滑入到一个元素时,该元素放大,该元素的前一个元素和后一个元素缩小,除了这三个元素之外的其他元素缩的更小并且有一定透明度; html 结构如下 <div class="box"> <div class="son">乔丹</div> <div class="son">科比</div> <div class="son">詹姆斯</div> <div class="son">奥尼尔</div> <div class="son">邓肯</div> <div class="son">卡特</div> <div class="son">麦迪</div> <div class="son">艾弗森</div> <div class="son">库里</div> <div class="son">杜兰特</div> </div>关键 css 代码 .son { ... ... ... &:hover { background-color: #67c23a; transform: scale(1.4); &+.son { transform: scale(1.1); // 后一个相邻的兄弟元素 } } }让前一个元素也缩放为原来的 1.1 /* 选择存在 后一个相邻的被hover的兄弟元素 的元素 */ .son:has(+ .son:hover) { transform: scale(1.2); }然后对这三个元素之外的其他元素缩放为原来的 0.8 .box:has(.son:hover) .son:not(:hover, :has(+ :hover), .son:hover + *) { transform: scale(0.8); opacity: 0.7; }.box:has(.son:hover) 表示选择子元素 son 被 hover 时的 .box .son:not(:hover, :has(+ :hover), .son:hover + *) 表示排除 son 元素里面被 hover 的元素,被 hover 的元素的前一个邻接的兄弟元素,被 hover 的元素的后一个邻接的兄弟元素; 完整示例 4、CSS :has() 选择器之单选题 bb.gif图片 这是个有趣的应用,当选择的是错误的选项时,选择题的标题和当前选择项标红。并且会给正确的选项添加动画效果提示用户这才是正确选项。 这里用 data-correct="false" 表示错误的选项,data-correct="true" 表示正确的选项。 <input type="radio" name="option" data-correct="false" id="option1" /> <label for="option1">Responsive design</label> <input type="radio" name="option" data-correct="true" id="option2" /> <label for="option2">Responsive design</label> <input type="radio" name="option" data-correct="false" id="option3" /> <label for="option3">Responsive design</label>选择错误选项时,标红当前选项。选择正确选项时标绿当前选项。 .question { --correct: #5ed235; /* 正确选项的颜色 */ --wrong: #f83d56; /* 错误选项的颜色 */ --wrong-bg: rgba(248, 61, 86, 0.8); --correct-bg: rgb(94, 210, 53, 0.8); } input[data-correct="false"]:checked+label { color: #fff; background-color: var(--wrong); border-color: var(--wrong); } input[data-correct="true"]:checked+label { color: #fff; background-color: var(--correct); border-color: var(--correct); }选择错误选项时,标红标题; 这里用 :has 选择器获取子元素中有错误选项选中时。 .question:has(input[data-correct="false"]:checked) { .questionHeader { box-shadow: inset 0 7px 0 0 var(--wrong); background-color: var(--wrong-bg); } }并且给正确选项增加提示动画 .question:has(input[data-correct="false"]:checked) { input[data-correct="true"]+label { animation: flash 2s infinite; } } @keyframes flash { 0% { background-color: white; } 25% { background-color: #5ed235; } 50% { background-color: white; } 75% { background-color: #5ed235; } 100% { background-color: white; } }选择正确选项时,标绿标题; .question:has(input[data-correct="true"]:checked) { .questionHeader { box-shadow: inset 0 7px 0 0 var(--correct); background-color: var(--correct-bg); } }完整示例 总结 本文介绍了 :has()选择器的基本用法以及四个实际应用; 选择父元素和前面的兄弟元素 :has() 选择器中的 且 和 或 选择一个范围内的元素 在 :has()选择器出来之前,使用 CSS 是无法直接选择到父级元素和前面的兄弟元素的,但 :has()选择器的出现使这个变成了可能; 如果对本文感兴趣或对你有帮助,麻烦动动你们的发财手,点点赞~
-
浏览器Audio音频自动播放为什么会失效 背景 某天客户报了一个问题,说是大屏的声音不能自动播放了,我们的大屏应用是有报警的,当有报警的时候,会自动播放报警的声音 复线步骤 测试结果如下 当浏览页面后,音频不会自动播放 当从另外的一个页面进入到当前页面,可以直接播放声音 如果你想测试,可以点我进行测试 你可以先点击上面链接的 「尝试一下」,下面为截图 图片 这个时候你会听到一声马叫声 然后,你刷新下一个马叫声的页面,这个时候声音的自动播放将不会生效 图片 报错问题排查 打开控制台,无法意外看到一个报错信息。 图片 翻译为中文的意思为允许的错误。播放失败,因为用户没有先与文档交互。https\://goo.gl/xX8pDD 尝试解决 那我就通过给body添加点击事件,自动触发点击事件,在点击的事件后自动播放声音。(当是我的想法是,这个大概率是不行的,chrome应该不会这个忽略点,不然这个功能就外表不存在) 经过测试后,发现确实还不行,在意料中。 参考别人的网站,用抖音测试 点击我跳转抖音 想到了我们可以参考抖音,我用抖音进行测试,当你不应该做任何操作时,页面如下 图片 我们从这里总结得出的结论,这应该是浏览器的,需要查看官方文档,看看原因 刊物 点我查看chrome的官方文档 「我截取了一些关键信息」 图片 注意浏览器有一个「媒体互动指数」,这是浏览器自动计算的,该分结果,才会触发自动播放 「查看电脑的媒体互动指数」 在url上输入about://media-engagement,你会看到如下的截图, 图片 「经过测试后」当网站变成了「High」,音频会自动播放,不会播放失败。 这里解释了为什么有的网站可以自动播放声音,有的网站不可以自动播放声音 好吧,我们继续往下看,这个时候看到了一些关键的信息。 「作为人,我们不应该相信音频开发/视频播放会成功,要始终在播放的回落中来进行判断」 图片 看到这些,我们来修改抖音的实现。在播放声音的catch的时候,显示一个错误的弹窗,提示用户,当用户点击的时候,自动播放声音 this.alarmAudio = new Audio(require("@/assets/sound/alarm.mp3")); this.alarmAudio .play() .then(() => { this.notifyId && this.notifyId.close(); }) .catch((error) => { if (error instanceof DOMException) { // 这里可以根据异常类型进行相应的错误处理 if (error.name === "NotAllowedError") { if (this.notifyId) return; this.notifyId = Notification({ title: "", duration: 0, position: "bottom-left", dangerouslyUseHTMLString: true, onClick: this.onAudioNotifyConfirm, showClose: false, customClass: "audio-notify-confirm", message: "<div style='color:#fff;font-size:18px;cursor:pointer'>因浏览器限制,需<span style='color:#ff2c55'>点击打开声音</span></div>", }); } } });实现效果如下 图片 总结 在启用视频或者音频的时候,要始终不相信他会播放声音成功,并且添加catch处理异常场景,给用户友好的提示 或者视频音频的自动播放跟媒体互动指数有关(MEI),当媒体指数高,会自动播放,否则需要用户先交互后,音频才可以自动播放。 从一个页面window.打开另外一个页面可以自动播放声音,当刷新页面后,需要有高的MEI,音频才会自动播放,如果你需要在后台打开一个大屏的页面,正好可以这样设计,不要用页面跳转
-
一款功能丰富、界面美观的OA办公系统 介绍 OA-System Vue 开发的OA系统 具有工作流动态审批、加签、会签等工作流功能 具有文档预览、图片预览等功能 具有博客编写、预览、查看、搜索等功能 具有社区、问答等功能 具有OA系统常用功能 具有在线网盘等功能 支持审批流程、自由流程,审批日志,我的待办,我的已办,行政公告,Office文档预览,文档转PDF,图片压缩。 功能一览 具有工作流动态审批、加签、会签等工作流功能,可以对工作流程的审批业务进行评论/讨论 具有文档预览、图片预览等功能 具有博客编写、预览、查看、搜索等功能 具有社区、问答等功能 具有OA系统常用功能 具有在线网盘等功能 具有OA的移动端应用 工作流引擎完全手写且开源,可以类似钉钉/飞书那样,动态选择审批人员。 项目截图 图片 图片 图片 图片 图片 图片 图片 图片 图片 源码下载 隐藏内容,请前往内页查看详情
-
CSS 让 height 高度属性完美支持 auto 过渡动画 众所周知,有些属性是不支持过渡动画的,比如高度 auto div{ height: 0; transition: 1s } .wrap:hover div{ height: auto }效果如下 图片 要实现过渡效果,之前提供过一个 grid 布局方式,原理是利用 grid 的尺寸单位 1fr 支持过渡的特性 一、calc-size 函数 现在要实现 auto 的过渡效果,需要用到一个全新的 calc-size 函数 图片 看到这个函数,是不是和 calc 比较类似?没错,这是一个可以将一些关键词转换成具体尺寸的函数。 回到上面这个例子,只需要将高度改成 calc-size(auto) ,如下 div{ height: 0; transition: 1s } .wrap:hover div{ height: calc-size(auto) }现在就有过渡效果了(Chrome 129+ 或者 Chrome 127+开启实验属性) 图片 其实除了 auto ,还支持其他尺寸关键词,比如 height: calc-size(min-content) height: calc-size(max-content) height: calc-size(fit-content)也支持混合计算,如下 height: calc-size(auto + 10px) height: calc-size(max-content - 10px)二、interpolate-size 属性 前面的例子,为了兼容之前的浏览器,还必须保留 height: auto 的写法 div{ height: 0; transition: 1s } .wrap:hover div{ height: auto; height: calc-size(auto) }如果是已经存在的项目,可能会有很多地方需要都要改成这种写法,有一定的侵入性。 为此,浏览器还提供了一个 interpolate-size 属性,这个属性可以设置插值计算的规则,有两个关键词 interpolate-size: numeric-only;/*默认值*/ interpolate-size: allow-keywords;其中第一个 numeric-only ,表示仅限数值,也就是只有真实的数值才会有过渡效果(目前浏览器的默认效果),第二个 allow-keywords 表示允许所有关键词,当然包括 auto 属性 有了这个属性,要做的事情就更简单了,只需要在全局 :root 加上这个属性 :root{ interpolate-size: allow-keywords; } div{ height: 0; transition: 1s } .wrap:hover div{ height: auto; }这样就全局生效了,无需在每个地方就加上 calc-size(auto) ,是不是非常方便呢? 图片 三、也支持 detail 展开过渡 大家可能都知道,detail 配合 summary 可以实现展开折叠效果 <div class="con"> <details name="a"> <summary>分组A</summary> <p>这是第一个分组 name="a"</p> </details> <details name="a"> <summary>欢迎</summary> <p>最近 details元素新增了一个name属性</p> </details> <details name="a"> <summary>关注</summary> <p>别看这只是一个普普通通的属性,这可是带来了一个全新的模式,一起了解一下吧</p> </details> </div>效果如下 图片 当然默认展开折叠是没有过渡效果的 利用 calc-size 或者 interpolate-size 也可以很轻松的实现过渡动画,关键实现如下 :root { interpolate-size: allow-keywords; } ::details-content{ content-visibility: visible; height: 0; transition: .3s; overflow: hidden; } details[open]::details-content{ height: auto; }这样就有过渡效果了,非常丝滑 图片 这是一段通用代码,可以用在任何地方 四、现在其实可以用起来了 虽然说兼容性很差(Chrome 129+),但这是一个渐进增强属性,不会影响现有功能,也无需修改已有结构,只需要全局增加这样一行就行了 :root { interpolate-size: allow-keywords; }这样能支持的浏览器自然就会有过渡动画了,完全不用担心是否兼容。 总结一下,其实就两点 calc-size 可以将非数值类型的单位转换成支持过渡的尺寸单位,包括 auto interpolate-size 可以从全局范围允许任意关键词支持过渡
-
Three.js 可滑动魔方源码 图片 html 部分 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔方</title> <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="mofang"> <div class="mofang__background"></div> <div class="mofang__game"></div> <div class="mofang__texts"> <h1 class="text text--title"> <span>魔方</span> </h1> <div class="text text--note"> 双击即可开始 </div> <div class="text text--timer"> 0:00 </div> <div class="text text--complete"> <span>完成!</span> </div> <div class="text text--best-time"> <icon trophy></icon> <span>最佳时间!</span> </div> </div> <div class="mofang__prefs"> <range name="flip" title="翻转类型" list="迅速的 ,平滑的,弹起"></range> <range name="scramble" title="扰乱长度" list="20,25,30"></range> <range name="fov" title="摄像机角度" list="摄影,透视的"></range> <range name="theme" title="配色方案" list="灰色,浅蓝色,浅黄色,脏黄色,淡青色"></range> </div> <div class="mofang__stats"> <div class="stats" name="total-solves"> <i>全部解决:</i><b>-</b> </div> <div class="stats" name="best-time"> <i>最佳时间:</i><b>-</b> </div> <div class="stats" name="worst-time"> <i>最差时间:</i><b>-</b> </div> <div class="stats" name="average-5"> <i>平均值 5:</i><b>-</b> </div> <div class="stats" name="average-12"> <i>平均值 12:</i><b>-</b> </div> <div class="stats" name="average-25"> <i>平均值 25:</i><b>-</b> </div> </div> <div class="mofang__buttons"> <button class="btn btn--bl btn--stats"> <icon trophy></icon> </button> <button class="btn btn--bl btn--prefs"> <icon settings></icon> </button> <button class="btn btn--bl btn--back"> <icon back></icon> </button> <a href="#" class="btn btn--br btn--pwa"> </button> </div> </div> <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js'></script> <script src="js/common.js"></script> </body> </html>完整项目地址 隐藏内容,请前往内页查看详情
-
JavaScript逆向系列 09-Js Hook 0x00 前言 缘起是我前两天看了一篇逆向文章,里面用到了一段hook脚本,在这之前我是不理解hook是什么东西,看了那段脚本后我突然就悟了,遂写下这篇文章分享一下个人心得。 注:本文不会太深入js hook。 0x01 Intro 首先我需要明确一点,我们在浏览网站时,由于js是在客户端上执行的,所以我们有最高的权限,这也就方便我们去对js进行一些操作,例如hook。我个人认为的js hook技术就是我们去修改函数或方法内部的实现过程,也就是重写方法,以便我们去利用它去实现一些我们想要的效果。 接下来我会通过一个小demo简单演示一下js hook脚本该如何编写。 0x02 Demo console.log = function (message) { alert(message); }效果: 图片 console是内置对象,不需要实例化即可使用,所以我们可以直接修改它的方法,例如log方法。代码中我将log方法的内部实现过程改成了将传入的参数进行alert。不过这个脚本其实还是有缺陷的,一般情况下我们改写某个函数或方法的内部实现过程后,我们还需要将它原本的功能实现了,否则可能会造成意想不到的后果。 改写后的脚本: var test = console.log; console.log = function (message) { alert(message); test(message); }将log方法赋值给test,代码执行完我们想要的效果后就完成它本来的工作,也就是最后执行test函数,相当于执行了log方法。 效果: 图片 图片 现在就是一个比较完善的hook脚本了。 0x03 个人脚本分享 我在看完上文提到的那篇逆向文后,立马就去写了我的第一个hook脚本(注:后文会发全部代码以及讲解如何使用): 图片 就拿上期自动化加解密的案例进行测试: 图片 图片 通过控制台输出的堆栈信息直接定位到解密位置。 我先说明一下我为什么要重写JSON.parse和JSON.stringify这两个方法,可能有看过我的逆向文章或者看过其他js逆向文章的朋友们会发现,这两个方法在加解密操作中出场率极高。一般情况下密文解密后开发可能会选择将json转为object,那么就会用到JSON.parse,JSON.stringify可能就会在加密时用到。 代码的话通过上文的讲解读者应该能看懂大部分,我觉得唯一需要讲的就是new Error().stack,Error是构造函数,需要实例化才能用,以下是MDN介绍的stack方法:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Error/stack 图片 简单来说就是它可以直接输出调用的堆栈,也就是上文演示的效果: 图片 不过我这里还需要提的一点的就是这个方法需要不断的实例化调用,如果只实例化一个对象然后反复调用这个实例的stack方法,只会输出实例化时的那个调用堆栈,例如: 图片 图片 0x04 如何使用js hook脚本 当我们要hook时,一般来说这个脚本一定要是文档中第一个加载执行的js,这样就能拿到函数或方法的控制权,所以我们就需要插件进行帮助。 这里我推荐大家使用油猴插件,直接在google应用商店搜篡改猴即可: 图片 安装后固定到chrome后点击添加新脚本: 图片 将代码写到注释部分即可: 图片 另外我需要提一点,我这个hook脚本一定要是文档中第一个加载的js,所以要修改一下脚本的运行时期。保存代码后点击设置: 图片 图片 修改运行时期即可,document-start是我们需要设置的: 图片 具体为什么可查看油猴中文文档(https://www.cnblogs.com/grubber/p/12560522.html): 图片 hook_JSON: // ==UserScript== // @name hook_JSON // @namespace http://tampermonkey.net/ // @version 2024-10-29 // @description try to take over the world! // @author 0xsdeo // @match http://*/* // @icon https://www.google.com/s2/favicons?sz=64&domain=csdn.net // @grant none // ==/UserScript== (function() { 'use strict'; var json_p = JSON.parse; JSON.parse = function(str){ console.log(str); console.log(new Error().stack); console.log("-----------------------------------------------------------------------------------------------------") return json_p(str); } var json_s = JSON.stringify; JSON.stringify = function(obj){ console.log(obj); console.log(new Error().stack); console.log("-----------------------------------------------------------------------------------------------------") return json_s(obj); } })();最后提一点,脚本需要指定一下要执行脚本的网站,匹配到的网站才会执行脚本,也是在设置里改: 图片 这里我设置的*,意为所有网站都执行该脚本。
-
20个超好看又开源的落地页/首页模板(附源码) 分享 20 个超好看的落地页/首页模板。 模板预览 nefa 图片 trippi 图片 tailwind-landing-page-template 图片 vivid-landing-template 图片 shadcn-landing-page 图片 open-react-template 图片 skilline-landing-page 图片 template-landing-page 图片 SaaS-Boilerplate 图片 nextly-template 图片 fresh 图片 landing 图片 nutritrack 图片 mylandingpage 图片 React-Landing-Page-Template 图片 landy-react-template 图片 landing-template-nextui 图片 next-saas-starter 图片 daisyui-nextjs-landing-page 图片 react-landing-page-template-2021 图片 源码下载 隐藏内容,请前往内页查看详情