分类 学习笔记 下的文章

需求

在某项目开发过程中,产品妹子提了一个需求,要抄支付宝账单,大概看了一下,主要是吸顶的部分不太好做。

刚开始打算用Javascript一把梭,后来想起来CSS3有一个position: sticky可以做到吸顶,直接搞定收工。

等等,让我再看看设计稿……

啊,还要修改吸顶时的样式!还是谷歌一下吧。

解决方案

最终从这里找到了一个Solution。

.myElement {
  position: sticky;
  top: -1px;
}

.is-pinned {
  color: red;
}
const el = document.querySelector(".myElement")
const observer = new IntersectionObserver( 
  ([e]) => e.target.classList.toggle("is-pinned", e.intersectionRatio < 1),
  { threshold: [1] }
);

observer.observe(el);

原理

浏览器给我们提供了一个API用来观察元素是否在可视区域内,IntersectionObserver构造函数第一个参数是当元素可见时的回调函数,它的参数所携带的intersectionRatio属性值为1即监听对象完全可见,为0即完全不可见。

构造函数第二个参数是配置项,threshold属性值数组里的1表示当何时执行回调函数(或者说是执行函数的最低可视比例,0的时候元素刚刚进入可视区域就执行,1的时候元素完全进入可视区域才会执行)。

上述的解决方案使用了一个巧妙的方式,让吸顶时的元素top: -1px。当元素进入可视区域但未滚动到吸顶位置时,它完全可视;当元素吸顶时由于顶部向上偏移了1像素实际上没有完全可视,这样就完成了样式的切换。

要注意的是:回调函数的参数和配置项的threshold属性都是数组

关于IntersectionObserver的更多内容,请移步阮一峰大佬的博客

本文以主轴为横轴flex-direction:raw;进行解释,当主轴为纵轴flex-direction:column;时,请将本文宽度概念改为高度概念

  • flex: flex-grow | flex-shrink | flex-basis;

flex-grow:空间有余(父级容器宽度 > 当前元素总宽度)时如何分配
flex-shrink:空间不足(父级容器宽度 < 当前元素总宽度)时如何分配
flex-basis:空间分配基准值

空间有余时 flex-shrink 无效,空间不足时 flex-grow 无效,这两个属性是针对两种不同场景的互斥属性。

值:

  • 默认值: flex:0 1 auto;
  • none: flex:0 1 auto;
  • auto: flex: 1 1 auto;

一个值:

  • 非负数字 x: flex: x 1 0%;
  • 长度或百分比 y: flex: 1 1 y;

两个值:

  • 非负数字 m n: flex: m n 0%;
  • 非负数字 a 长度或百分比 b: flex: a 1 b;

flex-basis:
定义了在分配多余空间之前,项目占据着主轴空间。浏览器根据这个属性,计算主轴是否有多余空间。

flex-basis 与 width 同时设置时,flex-basis 优先级更高。
  • auto: 如果有 width 则为 width,否则根据内容,撑开宽度
  • 0: 根据内容,撑开宽度

请注意浏览器的兼容性

去除滚动条

<style>
  .scroll-view::-webkit-scrollbar{
    display:none;
  }
</style>

img图片适应

object-fit的属性:

  • fill(不保持纵横比缩放图片,使图片完全适应)
  • contain(保持纵横比缩放图片,使图片的长边能完全显示出来)
  • cover(保持纵横比缩放图片,只保证图片的短边能完全显示出来)
  • none(保持图片宽高不变)
  • scale-down(当图片实际宽高小于所设置的图片宽高时,显示效果与none一致;否则,显示效果与contain一致)
  • inherit
  • initial
  • unset
<style>
  .image{
    object-fit: cover;
  }
</style>

input框placeholder样式

<style>
  .input::-webkit-input-placeholder {
    color: red;
  }
</style>

nth-child

<style>

  /* 奇数子元素 */
  .item:nth-child(odd) {
    background-color: #f66;
  }

  /* 偶数子元素 */
  .item:nth-child(even) {
    background-color: #66f;
  }

  /* 第2个元素 */
  .item:nth-child(2) {
    background-color: #3c9;
  }

  /* 小于等于2的元素 */
  .item:nth-child(-n+2) {
    background-color: #3c9;
  }

  /* 大于等于2的元素 */
  .item:nth-child(n+2) {
    background-color: #3c9;
  }


  /* 从第6个元素到第10个元素 */
  .item:nth-child(n+6):nth-child(-n+10) {
    background-color: #3c9;
  }

  /* 倒数第2个元素 */
  .item:nth-last-child(2) {
    background-color: #3c9;
  }

</style>

:not

<style>
  
  /* 选择除了最后一个元素的其他元素 */
  .item:not(:last-child) {
    background-color: #f66;
  }

</style>

文字裁剪

<style>

  /* 单行文字裁剪 */
  .text{
    overflow: hidden; 
    text-overflow:ellipsis; 
    white-space: nowrap;
  }

  /* 多行文字裁剪 */
  .text{
    display: -webkit-box;
    -webkit-box-orient: vertical;
    /* 裁剪行数 */
    -webkit-line-clamp: 2;
    overflow: hidden;
  }

</style>

阻止文字被选择

<style>
  .text{
    user-select:none;
  }
<style>

吸附定位

目标元素在可视区域内表现为relative ,滚动到可视区域外表现为fixed

<style>
  .topbar{
    position:sticky;
    top:0;
  }
<style>

防抖

长时间触发同一事件,该事件方法只执行一次。

function debounce(func,wait){
    let timeout = null;
    return function(){
        // 使this指向不变并能获取到e参数
        let context = this;
        let args = arguments;
        if(timeout){
            clearTimeout(timeout);
        }
        timeout = setTimeout(() => {
            func.apply(context, args);
        },wait);
    }
}

function a(e){
    console.log('im a');
    console.log(e);
}

window.onmousemove = debounce(a,1000);

节流

长时间触发同一事件,该事件方法每间隔一定时间执行一次。

function throttle(func, wait) {
    let timeout;
    return function() {
        let context = this;
        let args = arguments;
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(context, args);
            }, wait);
        }
    }
}

function a(e){
    console.log('im a');
    console.log(e);
}

window.onmousemove = throttle(a,1000);

代码转自这里

正则使用正则表达式:

可以输入数字和小数点后有两位以内的小数: /^(([^0][0-9]+|0)\.([0-9]{1,2})$)|^([^0][0-9]+|0)$/

只能输入非零开头的纯数字: /^([^0][0-9]+|0)$/

只能输入带小数点的小数: /^(([^0][0-9]+|0)\.([0-9]{1,2}))$/

Vue 指令:

directives: {
  // 限制只能输入整数
  inputInt: {
    bind(el, binding, vnode) {
      let input = vnode.elm;
      input.addEventListener('compositionstart', () => {
        vnode.inputLocking = true
      })
      input.addEventListener('compositionend', () => {
        vnode.inputLocking = false
        input.dispatchEvent(new Event('input'))
      })
      input.addEventListener('input', () => {
        if(vnode.inputLocking) {
          return;
        }
        let oldValue = input.value;
        let newValue = input.value.replace(/[^\d]/g, '');
        if(newValue && binding.value !== 'zeroBefore') {
          newValue = newValue.replace(/^\b(0+)/gi, '') // 不指定可以以0开头的时候 去掉开头多余的0
        }
        // 判断是否需要更新,避免进入死循环
        if(newValue !== oldValue) {
          input.value = newValue
          // 通知v-model更新 vue底层双向绑定实现的原理是基于监听input事件
          input.dispatchEvent(new Event('input')) 
        }
      })
    }
  },
  // 限制能输入小数点后两位的正小数
  inputFloat: {
    bind(el, binding, vnode) {
      let input = vnode.elm;
      input.addEventListener('compositionstart', () => {
        vnode.inputLocking = true
      })
      input.addEventListener('compositionend', () => {
        vnode.inputLocking = false
        input.dispatchEvent(new Event('input'))
      })
      input.addEventListener('input', () => {
        if(vnode.inputLocking) {
          return;
        }
        let oldValue = input.value;
        let newValue = input.value;
        newValue = newValue.replace(/[^\d.]/g, '');
        newValue = newValue.replace(/^\./g, '');
        newValue = newValue.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.');
        newValue = newValue.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3')
        if(newValue) {
          newValue = newValue.replace(/^\b(0+)/gi, '') // 去掉开头多余的0
        }
        // 判断是否需要更新,避免进入死循环
        if(newValue !== oldValue) {
          input.value = newValue
          input.dispatchEvent(new Event('input')) // 通知v-model更新
        }
      })
      // input 事件无法处理小数点后面全是零的情况 因为无法确定用户输入的0是否真的应该清除,如3.02。放在blur中去处理
      input.addEventListener('blur', () => {
        let oldValue = input.value;
        let newValue = input.value;
        if(newValue) {
          newValue = Number(newValue).toString()
        }
        // 判断是否需要更新,避免进入死循环
        if(newValue !== oldValue) {
          input.value = newValue
          input.dispatchEvent(new Event('input')) // 通知v-model更新
        }
      })
    }
  }
}