样式穿透和实现固钉效果

最近在写代码时遇到一个问题,就是我想要实现一个固钉效果,让某个元素在超过屏幕时会悬浮在屏幕上的某个位置。这就类似与flex布局。通常情况下许多的ui组件库都有提供affix组件来实现固钉效果,就比如element plus给我们提供的el-affix组件就有这样的效果,但是在某些情况之下el-affix就会失效。

在我们通常的vue项目中为了防止我的些的css样式冲突,我们都会在style上加上scoped

<style lang="scss" scoped>
<style>

关于scoped

就像这样,这时可能就会出现el-affix组件失效,其原因就是因为我加上了scoped这个属性,因为vu是单页面应用,所以我们写的不同页面最后都会被打包成一个..html文件,所以你在不同页面定义的css样式最后都会被引入到这个html文件中,这就导致如果你在不同的文件中定义的相同的文件名称,就很有可能导致样式错乱。所以就有了scoped,他的原理就是通过给样式加上识别标签来进行分类,不同的页面识别标签也就不同,

如图div后面的一大串就是vue为我们加上的识别标签。

同样在引入的css文件中vue也会给我们加上识别标签,这样就不会出现样式名冲突的问题,但是我们有时在使用第三方样式库(比如element,ant等)时就会出现无法修改样式的问题,这是因为vue没有将识别标签加到组件上

vue只会在你的最外层上加上data-v-xxxxxx,但是在css文件中vue会帮你吧data-v-xxxxxx拼接在样式的最后一段。所以就导致了你都样式永远无法命中我们想要的元素,除非我们把scoped属性去掉,但是这样就可能倒是我们上面说的问题,所以我们不推荐。

样式穿透

这时我们就引入了另一个概念,就是样式穿透。样式穿透的使用方式很多,有>>>::v-deep/deep/等。在我们使用了样式穿透后vue就会把唯一的识别标志移到我们写::v-deep之后

.home_main {max-width: 1100px;margin: 48px auto 28px;display: flex;.page{&:deep(button){border-radius: 4px;background-color: #fff;box-shadow: 0 2px 1px -2px rgb(0 0 0 / 20%), 0 2px 2px 0 rgb(0 0 0 / 14%), 0 1px 5px 0 rgb(0 0 0 / 12%);}&:deep(.el-pager){.number{border-radius: 4px;background-color: #fff;box-shadow: 0 2px 1px -2px rgb(0 0 0 / 20%), 0 2px 2px 0 rgb(0 0 0 / 14%), 0 1px 5px 0 rgb(0 0 0 / 12%);}.more{border-radius: 4px;box-shadow: 0 2px 1px -2px rgb(0 0 0 / 20%), 0 2px 2px 0 rgb(0 0 0 / 14%), 0 1px 5px 0 rgb(0 0 0 / 12%);background-color: #fff;}.active{box-shadow: 0 2px 4px -1px rgb(0 0 0 / 20%), 0 4px 5px 0 rgb(0 0 0 / 14%), 0 1px 10px 0 rgb(0 0 0 / 12%);}}}

再看没有加样式穿透之前的样式

随意通过样式穿透我们就可以实现scoped所带来的副作用,顺便一提>>>的兼容性不是很高,所以不是很推荐,最好写后面两种。(因为最近在写vue3的项目,在vue3中提示使用&:deep(.name)来实现样式穿透,目前我也不知道他是vue3的特定写法还是,新的写法,就和大家分享一下。还有一个我觉得说样式穿透讲的挺好的视频https://www.douyin.com/video/6987146287137180966 如果还是有不懂什么是样式穿透以及原理的,推荐可以看看)

.el-card {&:deep(.el-card__body) {padding: 0;display: flex;height: 250px;align-items: center;}
}

说到这里大部分的样式问题都可以解决,但是当我去尝试el-effix时结果发现还是不行,弄得我快疯了,最后去看了el-affix的源码才知道,elememt plus 是通过判断el-affix指定的容器,当超过屏幕时给内层div添加样式,让el-affix变成悬浮的状态,在没超过屏幕的状态下移除div的悬浮样式

样式穿透可以解决当容器超出屏幕悬浮的问题,但是当回到原始位置时,就无法消除内层div的el-affix--fixed样式,导致出现一直悬浮的状态,可能是我技术还不够吧,如果有解决的朋友可以分享下。

我只好另找出路,既然element plus的组件用不了,那就自己写一个(其实是网上cv的),由于我的项目的vue3+ts的,但是网上大多都是vue2和js的所以就自己改了那么一点点,本人小白,写的不好的地方还请轻点喷,不多说,上代码

<template><div class="affix-placeholder" :style="data.wrapStyle"><div class="affix-div" :class="{'affix': data.affixed}" :style="data.styles"><slot /></div></div>
</template><script lang="ts">
import {computed,defineComponent,getCurrentInstance,onMounted,reactive,onUnmounted } from 'vue';
import { Styles, affixProps } from './affixProps';export default defineComponent({name: 'Affix',props: affixProps,setup(props: any) {const data = reactive({affixed: false,styles: {} as Styles,affixedClientHeight: 0,wrapStyle: {},});const instance = getCurrentInstance();const getScroll = function(w: Window, top?: boolean){let ret = w[`page${(top ? 'Y' : 'X')}Offset`];const method = `scroll${top ? 'Top' : 'Left'}`;if (typeof ret !== 'number') {const d = w.document;// ie6,7,8 standard moderet = d.documentElement[method];if (typeof ret !== 'number') {// quirks moderet = d.body[method];}}return ret;};const getOffset = function (element: Element) {const rect = element.getBoundingClientRect();const body = document.body;const clientTop = element.clientTop || body.clientTop || 0;const clientLeft = element.clientLeft || body.clientLeft || 0;const scrollTop = getScroll(window, true);const scrollLeft = getScroll(window);return {top: rect.bottom + scrollTop - clientTop - data.affixedClientHeight,left: rect.left + scrollLeft - clientLeft};};const offsets = computed(()=>{if (props.boundary) {return 0;}return props.offset;});const handleScroll = () => {const scrollTop = getScroll(window, true) + offsets.value; // handle setting offsetconst elementOffset = getOffset(instance?.proxy?.$el);if (!data.affixed && scrollTop > elementOffset.top) {data.affixed = true;data.styles = {top: `${offsets.value}px`,left: `${elementOffset.left}px`,width: `${instance?.proxy?.$el.offsetWidth}px`};props.onAffix(data.affixed);}// if setting boundaryif (props.boundary && scrollTop > elementOffset.top) {const el = document.getElementById(props.boundary);if (el) {const boundaryOffset = getOffset(el);if ((scrollTop + offsets.value) > boundaryOffset.top) {const top = scrollTop - boundaryOffset.top;data.styles.top = `-${top}px`;}}}if (data.affixed && scrollTop < elementOffset.top) {data.affixed = false;data.styles = {};props.onAffix(data.affixed);}if (data.affixed && props.boundary) {const el = document.getElementById(props.boundary.slice(1));if (el) {const boundaryOffset = getOffset(el);if ((scrollTop + offsets.value) <= boundaryOffset.top) {data.styles.top = 0;}}}};onMounted(()=> {data.affixedClientHeight = instance?.proxy?.$el.children[0].clientHeight;data.wrapStyle = { height: `${data.affixedClientHeight}px` };window.addEventListener('scroll', handleScroll);window.addEventListener('resize', handleScroll);});onUnmounted(()=>{window.removeEventListener('scroll', handleScroll);window.removeEventListener('resize', handleScroll);});return {data,};}
});
</script><style lang="sass">
.affixposition: fixed
</style>
import { PropType } from 'vue';
import { FunctionType } from '@/constant/Type';export interface Styles{top?: string | number,left?: string | number,width?: string | number,
}export const affixProps = {offset: {type: Number as PropType<number>,default: 0,},onAffix: {type: Function as PropType<FunctionType>,default() {}},boundary: {type: String as PropType<string>,default: 0,}
};export interface FunctionType {(): void;
}

这样一个affix组件就写完了,但是经过我的一轮测试,发现还是不行,最后发现是因为自定义的affix组件是在初始化时获取父级组件或绑定的父级组件的高度,但是我的父级组件的高度是动态的,通过后端获取数据后再通过v-for渲染,所以初始高度很小,在高度改变后的affix内的父级高度却没有发生变化。

sticky粘性布局

在经过了又一番的查找资料后,我又看到了一个解决方案,就是posistion:sticky在css的posistion中我们常用到的属性有relativeabsolute,sticky就在粘性布局我们可以通过设置

position: -webkit-sticky;
position: sticky;

这个属性来实现类似固钉的效果,在官方文档中的说明是sticky 英文字面意思是粘,粘贴,所以可以把它称之为粘性定位。

position: sticky; 基于用户的滚动位置来定位。

粘性定位的元素是依赖于用户的滚动,在 position:relative 与 position:fixed 定位之间切换。

它的行为就像 position:relative; 而当页面滚动超出目标区域时,它的表现就像 position:fixed;,它会固定在目标位置。

元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位。

这个特定阈值指的是 top, right, bottom 或 left 之一,换言之,指定 top, right, bottom 或 left 四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同。

反正看这么多也不是很懂先cv再说,不出意外的话就要出意外了,果然不行,没办法只能再去看文档了,最后才知道posistion:stucky要生效也需要有条件,总结来说

  1. 须指定 top, right, bottom 或 left 四个阈值其中之一,就是需要设置这几个元素任意一个值,并且 top 和 bottom 同时设置时,top 生效的优先级高,left 和 right 同时设置时,left 的优先级高。
  2. 最重要的一点,在设置了posistion:stucky后该元素的所有父级元素都不能设置overflow:hidden不然也不会生效,具体可以查看官方文档。

最后终于可以了,就这一个问题困了我三天,本以为事情就这样结束了,结果过几天天我在加其他的功能时突然发现,又失灵了。。。。。

最后排查了好久才发现原来是原先在设置el-scrollbaroverflow时没有设置成功,再加一个

overflow: visible !important;

终于所有问题都解决了。

样式穿透和实现固钉效果相关推荐

  1. antd源码-Affix固钉解析

    antd源码-Affix固钉解析 固钉其实按照我自己的理解就是用固定定位将其定位到某个位置.很简单的一个效果. antd-affix我认为其核心可以概括为几点: 组件加载滚动监听,组件销毁销毁监听. ...

  2. vue3 + vite + ts + setup , 第十二练 Vue3 css style 新特性,样式穿透,插槽选择器,全局选择器,在vue3中使用tailwindcss

    一.vue3.x 样式新特性 样式穿透  深度选择器 <style scoped> .a :deep(.b) {/* ... */ } </style> 插槽选择器 <s ...

  3. css样式穿透(深度选择器)

    1. 什么情况下使用样式穿透 引入第三方组件库(如element-ui.element-plus),修改第三方组件库的样式 样式文件中使用了 scoped 属性,但是为了确保每个组件之间不存在相互影响 ...

  4. vue、Affix 固钉、Affix 属性事件、vue Affix 全部固钉、vue Affix 全部属性事件

    vue.Affix 固钉.Affix 属性事件.vue Affix 全部固钉.vue Affix 全部属性事件 设计规则 何时使用 代码演示 1.基本 2.固定状态改变的回调 3.滚动容器 API 事 ...

  5. antd学习之Affix固钉的学习

    Affix的学习 这个组件有四个属性值,当我们的页面过长我们需要对某些元素进行固钉在某个位置不再进行跟着父级容器进行滚动时候使用 offsetTop={200}属性是当下拉页面的时候距离顶部200的时 ...

  6. 后台管理系统界面和样式,点击左边新建标签效果

    后台管理系统界面和样式,点击左边新建标签效果 Code <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&qu ...

  7. 带你玩转 ui 框架 ——scoped及样式穿透问题详解

    前言 在我们前端的开发中经常会使用到各种 ui 框架 下面这两个是比较火的,也是我常用的两个ui框架. 问题描述 但是在使用框架的时候难免会遇到需要改变组件中的一些样式,当然如果我们所有页面的组件样式 ...

  8. vue组件样式穿透种类 /deep/ ::v-deep >>>。the >>> and /deep/ combinators have been deprecated. Use :deep() in

    vue组件样式穿透种类 /deep/ ::v-deep >>> vue项目打包时警告:the >>> and /deep/ combinators have bee ...

  9. 【Vue】样式穿透 ::v-deep的具体使用

    [Vue]样式穿透 ::v-deep的具体使用 之前在项目中用到了 vant,使用特别简单,而且组建也非常的丰富.即时这样,在项目中肯定也需要用额外的样式来打造自己的应用. 直接在 中编写的话只会影响 ...

  10. vue中样式穿透的三种写法

    在vue项目中,当我们使用第三方组件库时,例如(element-ui.vant)时,遇到需要修改组件中第三方组件库中的样式,但同时又不想去除scoped属性造成组件之间的样式覆盖,这时,我们就可以使用 ...

最新文章

  1. C# 空合并运算符 ??
  2. sql server安装记
  3. php树莓派魔镜,用树莓派和显示器制作一面“魔镜”
  4. thinkserver rd650管理口地址_路由器WAN口和LAN口有什么区别【区别介绍】
  5. Skyfire-在移动设备上体验silverlight的效果
  6. 湖南师范大学计算机网络基础教学平台,基于网络的师徒式教学平台的设计与实现...
  7. 论文笔记:微表情识别综述1
  8. 硬盘安装Linux(ubuntu,centos)
  9. [2021时空AI白皮书]时空人工智能:关键技术
  10. 显卡驱动程序如何更新
  11. 编写类的步骤编写测试类
  12. NISP一级知识点学习笔记总结
  13. [堆+贪心] CF596C. Wilbur and Points
  14. 悲情的AI四小龙,背后不是专利无用
  15. MTK keypad调试,扩张键盘IC AW9523
  16. 微信登录小程序获取openId
  17. 【实践】python 机器码 实现一机一码
  18. 50个经典的增长黑客策略
  19. java文件tree目录_java 遍历目录,操作文件 tree命令
  20. actuator微服务信息完善

热门文章

  1. android 镜像投屏开发,Android 投屏实现纪要
  2. 【深度理解】如何评价GAN网络的好坏?IS(inception score)和FID(Fréchet Inception Distance)
  3. stm32单片机按键消抖、长按、多击终极解决方案
  4. python 对两列互补的数据合并
  5. Dapper系列之三:Dapper的事务修改与删除
  6. python实现根据前序序列和中序序列求二叉树的后序序列
  7. golang switch使用
  8. 最新傻妞搭建方法以及常见问题2022年9月25
  9. 队列、栈以及双端队列
  10. ArcMap进行标记符号制作