offsetTop、offsetParent、scrollTop以及vue如何实现左右联动效果

2023-11-02

  1. offsetTop:元素到offsetParent顶部的距离

  2. offsetParent:距离元素最近的一个具有定位的祖宗元素(relative,absolute,fixed),若祖宗都不符合条件,offsetParent为body。如下图所示:获取child的offsetTop,图1的offsetParent为father,图2的offsetParent为body。在这里插入图片描述

  3. 注意:只有元素show(渲染完成)才会计算入offsetTop,若是中间有元素数据需要异步获取,会导致最终获取的offsetTop值偏小
    在这里插入图片描述

  4. offsetTop:获取当前滚动条滚动的距离
    tips:一定要在有属性 overflow:auto; 上绑定 ,否则无效
    例如:

	document.getElementById('testContent')
 	testContent.addEventListener('scroll', function (e) {
       const { scrollTop } = e.target
       console.log('scrollTop', parseInt(scrollTop))
    })

最终效果

在这里插入图片描述
checkList.vue

<template>
  <el-dialog
    id="check-list"
    :visible.sync="show"
    height="600px"
    width="958px"
    :before-close="close"
    >
    <template slot="title">
      <span>检查项设置</span>
    </template>
    <div class="check-list-body" id="constScroll">
      <el-form
        :model="ruleForm"
        :rules="rules"
        ref="ruleForm"
        size="small"
        label-width="150px">
        <div class="label">
            <span>a</span>
        </div>
        <el-row>
          <el-checkbox v-model="ruleForm.checked1" true-label="true" false-label="false">a</el-checkbox>
        </el-row>
        <el-row>
          <el-checkbox v-model="ruleForm.checked2" true-label="true" false-label="false">b</el-checkbox>
          <el-checkbox v-model="ruleForm.checked3" true-label="true" false-label="false">c</el-checkbox>
        </el-row>
        <el-row>
          <el-checkbox v-model="ruleForm.checked4" true-label="true" false-label="false">d</el-checkbox>
        </el-row>
        <div v-for="(item,index) in fromItems" :key="item.prop" :id="'leftTop'+ index">
          <div class="label">
            <span>{{item.label}}</span>
            <el-switch
              v-model="ruleForm[item.switch]"
              active-value="true"
              inactive-value="false"
              active-color="#13ce66"
              inactive-color="#ff4949">
            </el-switch>
          </div>
          <el-form-item
            :label="inputItem.label"
            prop="name"
            v-for="inputItem in item.children"
            :key="inputItem.prop">
            <el-input
              v-model="ruleForm[inputItem.prop]"
              style="width:255px;"
              placeholder=""
              readonly>
            </el-input>
          </el-form-item>
        </div>
      </el-form>
      <div class="check-list-body-right">
        <el-steps direction="vertical" finish-status="" process-status="">
          <el-step
            v-for="(item,index) in stepData"
            :key="index"
            @click.native="handleClickStep(index)"
          >
            <template slot="icon">
              <div :class="['step-icon', currentIndex === index ? 'select-style' : 'default-style']"></div>
            </template>
            <template slot="title">
              <span>{{item}}</span>
            </template>
          </el-step>
        </el-steps>
      </div>
    </div>
    <span slot="footer" class="dialog-footer">
      <el-button @click="close">取 消</el-button>
      <el-button type="primary" @click="determine">保 存</el-button>
    </span>
  </el-dialog>
</template>

<script>
import {
  RULE_FORM,
  FORM_ITEMS,
  STEP_DATA
} from './config.js'
export default {
  name: 'check-list',
  props: {
    show: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      fromItems: FORM_ITEMS,
      ruleForm: RULE_FORM,
      stepData: STEP_DATA,
      rules: [],
      leftTopArr: [],
      _scrollTop: 0,
      currentIndex: 0,
      isClickStep: false
    }
  },
  watch: {
    show(newVal) {
      this.getScrollTop()
      this.getLeftOffSetTop()
    }
  },
  mounted() {
  
  },
  methods: {
    handleClickStep(index) {
      this.currentIndex = index
      this.isClickStep = true
      // 点击右侧记录一个开关,当同步事件都执行完以后,恢复默认值
      setTimeout(() => {
        this.isClickStep = false
      })
      if (!index) {
        this.setScrollTop(0)
      }
      if (this.leftTopArr && this.leftTopArr.length > 0) {
        this.setScrollTop(this.leftTopArr[index])
        return
      }
      this.getLeftOffSetTop()
      this.setScrollTop(this.leftTopArr[index])
    },
    handleScrollTop() {
      if (this.leftTopArr && this.leftTopArr.length > 0) {
        for (let i = 0; i < this.leftTopArr.length; i ++) {
          const start = this.leftTopArr[i],
                end = this.leftTopArr[i + 1]
          if (this._scrollTop >= start && this._scrollTop <= end) {
            this.currentIndex = i
            return
          }
        }
        return
      }
    },
    determine() {
      this.$emit('close')
      this.isClickStep = false
    },
    close() {
      this.$emit('close')
      this.isClickStep = false
    },
    //设置窗口滚动条高度
    setScrollTop(top){
      if(!isNaN(top)) {
        document.getElementById('constScroll').scrollTop = top
      }
    },
    //获取系统参数内滚动高度
    getScrollTop(){
      this.$nextTick(() => {
        const testContentDom = document.getElementById('constScroll')
        testContentDom.addEventListener('scroll', (e) => {
          //点点击右侧时不触发滚动条事件
          if (!this.isClickStep) {
            const { scrollTop } = e.target
            this._scrollTop = parseInt(scrollTop)
            this.handleScrollTop()
          }
        })
      })
    },
    // 获取左侧每个div具体头部的高度
    getLeftOffSetTop(){
      this.$nextTick(() => {
        this.leftTopArr = [0]
        for(let index in this.fromItems) {
          const leftDoM = document.getElementById('leftTop'+ index)
          let offsetTopHeight = +(leftDoM.offsetTop) - 62 // 62高度是减去的表头
          this.leftTopArr.push(offsetTopHeight)
        }
      })
    }
  },
  destroyed() {
    // this.$nextTick(() => {
    //   const testContent = document.getElementById('constScroll')
    //   testContent && testContent.removeEventListener('scroll')
    // })
  }
}
</script>

<style lang="stylus" rel="stylesheet/stylus">
#check-list{
  .el-dialog{
    height: 600px;
    overflow: hidden;
    margin-top:unset;

    .el-dialog__header{
      text-align: left;
    }

    .el-dialog__body{
      overflow: hidden;
      height: calc(100% - 116px);
      padding: 10px 0 10px 20px;
    }

    .el-dialog__footer{
      border-top: 2px solid #f6f6f6;
    }
  }

  .check-list-body{
    display: flex;
    height: 100%;
    overflow: auto;

    .label{
      margin-bottom: 15px;

      span{
        margin-right: 10px;
      }
    }

    &-right{
      height: 300px;
      position: absolute;
      right: 100px;
      width: 130px;
      overflow: hidden;

      .step-icon{
        width: 10px;
        height:10px;
        border-radius:50%;
      }

      .default-style{
        background-color:#C0C4CC;
      }

      .select-style{
        width: 20px;
        height: 20px;
        border-radius:50%;
        background-color:#409eff;
      }

      .el-step__icon{
        height: 10px;
      }

      .el-step__line{
        height: 60px;
      }

      .el-step.is-vertical{
        align-items: center;
      }

      .el-step__icon.is-text{
        border: 0;
        border-color: #FFF;
        border-radius: 50%;
      }
    }
  }
}
</style>

config.js

export const RULE_FORM = {
  checked1: 'false',
  checked2: 'false',
  checked3: 'false',
  checked4: 'false',
  checked5: 'false',
  checked6: 'false',
  switch1: 'false',
  subjectName1: '',
  subjectName2: '',
  subjectName3: '',
  subjectCode1: '',
  subjectCode2: '',
}

export const FORM_ITEMS = [
  {
    label: 'b',
    switch: 'switch1',
    children: [
      {
        prop: 'subjectName1',
        code: 'subjectCode1',
        label: 'b1'
      },
      {
        prop: 'subjectName2',
        code: 'subjectCode2',
        label: 'b2'
      }
    ]
  },
  {
    label: 'c',
    switch: 'switch1',
    children: [
      {
        prop: 'subjectName1',
        code: 'subjectCode1',
        label: 'c1',
        isSelect: true
      },
      {
        prop: 'subjectName2',
        code: 'subjectCode2',
        label: 'c2'
      },
      {
        prop: 'subjectName3',
        code: 'subjectCode3',
        label: 'c3',
        isSelect: true
      }
    ]
  },
  {
    label: 'd',
    switch: 'switch1',
    children: [
      {
        prop: 'subjectName1',
        code: 'subjectCode1',
        label: 'd1'
      }
    ]
  },
  {
    label: 'e',
    switch: 'switch1',
    children: [
      {
        prop: 'subjectName1',
        code: 'subjectCode1',
        label: 'e1'
      },
      {
        prop: 'subjectName2',
        code: 'subjectCode2',
        label: 'e2'
      },
      {
        prop: 'subjectName3',
        code: 'subjectCode3',
        label: 'e3'
      },
      {
        prop: 'subjectName4',
        code: 'subjectCode4',
        label: 'e4'
      }
    ]
  },
  {
    label: 'f',
    switch: 'switch1',
    children: [
      {
        prop: 'subjectName1',
        code: 'subjectCode1',
        label: 'f1',
        isSelect: true
      },
      {
        prop: 'subjectName2',
        code: 'subjectCode2',
        label: 'f2'
      },
      {
        prop: 'subjectName2',
        code: 'subjectCode2',
        label: 'f3'
      }
    ]
  },
  {
    label: 'g',
    switch: 'switch1',
    children: [
      {
        prop: 'subjectName1',
        code: 'subjectCode1',
        label: 'g1'
      },
      {
        prop: 'subjectName2',
        code: 'subjectCode2',
        label: 'g2'
      }
    ]
  }
]

export const STEP_DATA = [
  'a',
  'b',
  'c',
  'd',
  'e',
  'f',
  'g'
]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

offsetTop、offsetParent、scrollTop以及vue如何实现左右联动效果 的相关文章

  • 为什么我的淘汰单选按钮在另一个具有点击绑定的元素内时会失败?

    我有一个单选按钮列表 我想要点击 li 他们还检查单选按钮 这一切都有效 直到我放了一个name单选元素上的属性 然后我的代码停止工作 我的代码如下所示 ul li li ul li
  • 是否存在 IsCallable 为 false 但 IsConstructor 为 true 的 JS 对象?

    ECMAScript 规范函数可调用 https www ecma international org ecma 262 6 0 index html sec iscallable当且仅当其参数具有 Call 内部方法时返回 true 它在
  • Javascript Promise“then”始终运行,即使 Promise 未能执行

    我希望当调用第二个 then 时不执行第三个 then 但是 即使 Promise 被拒绝 调用第二个 then 并且代码返回 rejected 然后返回 undefined 它仍然调用第三个 then 如何不运行第三个 then 这样 未
  • 为什么 iife 在一个简单的例子中不起作用?

    我不明白为什么函数表达式调用不起作用并抛出错误 你能给我解释一下吗 var a function x alert x function a 1 谢谢大家 任务比我想象的要容易得多 这是因为 JS 将 IIFE 解析为函数的参数调用 这样做时
  • 如何在react-bootstrap中禁用表单提交的

    在下面的代码片段中 我有许多文本类型的输入表单 如果用户点击 我似乎会得到相同的合成事件 就像他们按下提交按钮一样 我想忽略作为表单提交 只允许一个人按下 提交 按钮 我删除了一些表单组以减少示例 在所有情况下 按钮或 ENTER 键 e
  • 如何格式化 Highcharts 的 (x,y) 对数据的日期时间

    我的序列化方法会产生如下所示的日期时间字符串 2014 07 09T12 30 41Z 为什么下面的代码不起作用 function container highcharts xAxis type datetime series data x
  • 带有淘汰赛js的隐形recaptcha

    我正在完成隐形验证码 但我在实现它时遇到问题 谷歌开发人员页面中的代码显示它应该是这样的
  • 在打字稿中导入 json

    我是 typescript 的新手 在我的项目中 我们使用 typescript2 在我的要求之一中 我需要导入 json 文件 所以我创建了 d ts 文件如下 test d ts declare module json const va
  • 隐藏 Div 的父级

    我只是想隐藏父divcomments section div class content content green div div div 我试过这个 document getElementById comments section pa
  • 在 HTML5 画布中,如何用我选择的背景遮盖图像?

    我试图用画布来实现这一点 globalCompositeOperation 但没有运气 所以我在这里问 这里有类似的问题 但我没有在其中找到我的案例 我的画布区域中有图层 从下到上的绘制顺序 画布底座填充纯白色 fff 用fillRect
  • 检查 jQuery 1.7 中是否存在基于文本的选择选项

    所以我有以下 HTML 片段
  • window.location 和 location.href 之间的区别

    我对之间的区别感到困惑window location and location href 两者似乎都以相同的方式行事 有什么不同 window location是一个对象 它保存有关当前文档位置的所有信息 主机 href 端口 协议等 lo
  • 刷新页面时保存用户的选择

    我目前有一个页面显示不同团队的数据 我有一些数据 用户可以单击使其处于 打开 或 关闭 状态 并为每个数据显示不同的图标 它基本上就像一个清单 只是没有物理复选框 我想记住哪些 复选框 已被选中 即使在用户刷新页面或关闭浏览器并稍后返回之后
  • 如何使用 crypto-js 解密 AES ECB

    我正在尝试将加密数据从 flash 客户端 发送到服务器端的 javascript 在 asp 中作为 jscript 运行 有几个 javascript Aes 库 但它们实际上没有文档记录 我正在尝试使用 crypto js 但无法让代
  • 在 Javascript 中连接空数组

    我正在浏览一些代码 我想知道这有什么用处 grid push concat row 根据我的理解 它等同于 grid push row 为什么要大惊小怪 连接 你想使用 concat当您需要展平数组并且没有由其他数组组成的数组时 例如 va
  • 在 Shopify 商店中嵌入 Vue 组件

    在产品页面中 我尝试显示自定义 Vue 组件 为简洁起见 该组件根据给定的产品 ID 显示 Firebase 数据库中的一些信息 我最初尝试将其制作为 Shopify 应用程序 以便我可以访问他们的 API 我实现了 OAuth 并且可以检
  • 将 CKEditor 5 与 nuxtjs 结合使用

    我正在尝试在我的 Nuxtjs 项目中导入 CKEditor 5 的自定义版本 并且我已经尝试了所有可能的方法来正确导入它 但没有一个对我有用 这是其中之一 let ClassicEditor let CKEditor if process
  • 如何用另一个响应替换窗口的 URL 哈希?

    我正在尝试使用替换方法更改哈希 URL document location hash 但它不起作用 function var anchor document location hash this returns me a string va
  • 在 CKEditor 中设置字体大小和字体系列

    我正在使用 ckeditor 我想问一下这个插件如何设置font family和font size 我尝试过使用 CKEDITOR config font defaultLabel Arial CKEDITOR config fontSiz
  • 使用 MongoDB 和 Nodejs 插入和查询日期

    我需要一些帮助在 mongodb 和 nodejs 中按日期查找记录 我将日期添加到抓取脚本中的 json 对象 如下所示 jsonObj last updated new Date 该对象被插入到 mongodb 中 我可以看到如下 la

随机推荐