2023省赛

87 阅读7分钟

递归dom树

rotate -transform

Array的方法

Array.prototype.flat() - JavaScript | MDN (mozilla.org)

grid布局

选择器

  • [attr^="example"] 表示以^开头,[attr~="example"] 表示含有“example”属性,[attr*="example"] 含有这个字符串的属性,在任何位置都可以
  • [attr~=value]: 选择具有指定属性并且属性值中包含被空格分隔的指定值的元素。例如,<div class="example class"> 可以通过 [class~=example][class~=class] 选择器选择到。
  • [attr|=value]: 选择具有指定属性并且属性值以指定值开头,后面跟着连字符或属性值正好是指定值的元素。例如,<div class="example"> 可以通过 [class|=example] 选择器选择到。
  1. [attr^=value]: 选择具有指定属性并且属性值以指定值开头的元素。例如,<div class="example"> 可以通过 [class^=ex] 选择器选择到。
  2. [attr$=value]: 选择具有指定属性并且属性值以指定值结尾的元素。例如,<div class="example"> 可以通过 [class$=ple] 选择器选择到。
  • [attr*=value]: 选择具有指定属性并且属性值中包含指定值的元素。例如,<div class="example"> 可以通过 [class*=exa] 选择器选。
  • [attr~=value][attr*=value] 是两种不同的属性选择器,它们的区别如下:
  • [attr~=value]:这是一个包含选择器(Attribute Contains Selector)。它选择具有指定属性,并且属性值中包含被空格分隔的指定值的元素。换句话说,它只会匹配属性值中包含整个指定值的元素,其中指定值可以是一个词列表,但是这些词必须以空格分隔。例如,[class~=example] 选择器将会匹配 <div class="example class"> 这样的元素,因为 "example" 是 class 属性值的一部分。
  • [attr*=value]:这是一个包含子串选择器(Attribute Contains Substring Selector)。它选择具有指定属性,并且属性值中包含指定值的任何部分的元素。换句话说,它匹配属性值中包含指定值的任何元素,无论指定值处于属性值的哪个位置。例如,[class*=ex] 选择器将会匹配 <div class="example"> 这样的元素,因为 "example" 中包含 "ex"。

因此,[attr~=value] 匹配整个属性值中包含指定值的元素,而 [attr*=value] 匹配属性值中的任何部分包含指定值的元素。

ElementPlus

setTimeout

  1. setTimeout 函数会在指定的时间间隔后执行一次指定的函数或指定的代码。语法如下:
javascriptCopy Code
setTimeout(function, milliseconds);

其中 function 是要执行的函数或代码,milliseconds 是延迟的毫秒数。

示例:

javascriptCopy Code
setTimeout(function() {
    console.log('Hello, world!');
}, 1000); // 在 1000 毫秒(1 秒)后输出 "Hello, world!"
  1. setInterval 函数会在每个指定的时间间隔后重复执行指定的函数或指定的代码。语法如下:
setInterval(function, milliseconds);

其中 function 是要执行的函数或代码,milliseconds 是时间间隔的毫秒数。

示例:

setInterval(function() {
    console.log('Hello, world!');
}, 1000); // 每隔 1000 毫秒(1 秒)输出 "Hello, world!"

总的来说,setTimeout 用于在一定的延迟后执行一次代码,而 setInterval 则用于在一定的时间间隔内重复执行代码。在使用定时器时,需要注意避免产生内存泄漏和不必要的资源消耗。

布局

image.png

#box1{
  display: flex;
  justify-content: center;
  align-items: center;
}

#box2{
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
#box2 span:nth-child(1){
  align-self: flex-start;
}
#box2 span:nth-child(2){
  align-self: flex-end;
}


#box3{
  display: flex;
  flex-direction: column;
}

#box3 span:nth-child(1){
    
}
#box3 span:nth-child(2){
 align-items: center;
 align-self: center;
}

#box3 span:nth-child(3){
  align-self: self-end;
}

#box1{
  display: flex;
  justify-content: center;
  align-items: center;
}

#box2{
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
}
#box2 span:nth-child(1){
  grid-area: 1/1;
}
#box2 span:nth-child(2){
  grid-area: 3/3;
}

#box2 span:nth-child(3){
  grid-area: 3/3;
}

#box3{
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
}

#box3 span:nth-child(1){
  grid-area: 1/1;
}
#box3 span:nth-child(2){
  grid-area: 2/2;
}

#box3 span:nth-child(3){
  grid-area: 3/3;
}

align-self是在自己所在的小块里的位置

image.png

Object && NodeList


$(function () {
  // 使用 ajax 获取 userList.json 数据并渲染到页面
  getData();
  const rightSelect = document.querySelector('#rightSelect')
  const leftSelect  = document.querySelector('#leftSelect')
 
  // 为按钮添加事件
  $("#add").click(function () {
    // TODO:补充代码,实现功能\
    changeAccess(true,leftSelect.querySelectorAll( 'option:checked'))
  });
  $("#addAll").click(function () {
    // TODO:补充代码,实现功能
    changeAccess(true,leftSelect.querySelectorAll('option'))
  });
  $("#remove").click(function () {
    // TODO:补充代码,实现功能
    changeAccess(false,rightSelect.querySelectorAll('option:checked'))
  });
  $("#removeAll").click(function () {
    // TODO:补充代码,实现功能
    changeAccess(false,rightSelect.querySelectorAll('option'))
  });
});
/**
 * 修改权限
 * @param {Object} right 要修改的权限
 * @param {Object} changeList 要修改权限的用户列表
 */
function changeAccess(right, changeList) {
  // TODO:补充代码,实现功能
  console.log(changeList,"changeList")
  const tr = Array.from(document.querySelectorAll('tr'))
  const ChangeL = Array.from(changeList)
  Array.from(changeList).forEach(item=>{
    // // 左边移到右边
    // if(right){
    //   rightSelect.appendChild(item)
    //   leftSelect.removeChild(item)
    // }else{
    //   leftSelect.appendChild(item)
    //  rightSelect.removeChild(item)
    // } 在第一段代码中,当权限为右边时,直接将选项元素从左边的选择框移动到右边的选择框中,使用 appendChild 方法实现。但这样做会导致选项从左边移动到右边时,在左边选择框中将不再存在该选项。这意味着该选项在两个选择框中都不能存在,因为一个元素只能在文档中的一个位置存在。因此,如果你想在右边选择框中显示相同的选项,你需要在移动之前创建一个选项的副本,并将其插入到右边选择框中。

// 而在第二段代码中,通过使用 cloneNode(true) 方法创建了选项元素的一个副本,并将该副本插入到目标选择框中。这样做不仅能够保留左边选择框中的选项,还能够在右边选择框中显示相同的选项。这种方式更加安全和可靠,因为它保留了原始选项元素,并在移动时使用了它的副本。
    if(right){
      rightSelect.appendChild(item.cloneNode(true))
      leftSelect.removeChild(item)
    }else{
      leftSelect.appendChild(item.cloneNode(true))
     rightSelect.removeChild(item)
    }
    const tar = tr.filter(trItem=>{
    const firstTd = trItem.querySelector('td:first-child'); // 获取当前<tr>元素下的第一个<td>元素
    return firstTd.innerText===item.innerText
    })
    tar[0].querySelector('td:nth-child(2)').innerText = right?"管理员":"普通用户"
  })
}
// 异步获取数据
async function getData() {
  // TODO:补充代码,实现功能
  let res = await fetch("./js/userList.json")
  let data = await res.json()
  data.forEach((e,i) => {
    let dom  =''
    dom +=`
    <tr>
    <td>${e.name}</td>
    <td>${e.right?"管理员":"普通用户"}</td>
  </tr>
    `
    // const td1 = document.createElement('td')
    // const tr = document.createElement('tr')
    // const td2 = document.createElement('td')
    // td1.innerText = e.name
    // td2.innerHTML = e.right?"管理员":"普通用户"
    // tr.appendChild(td1)
    // tr.appendChild(td2)
    userList.appendChild(tr)
    console.log(`第${i}次`,tr,userList)
  });
  consolelog(data)
}

骨架屏幕 -vue递归组件

<div :class="'ske-'+paragraph.type+'-container'">
<div class="ske" v-for="item in paragraph.rows??paragraph.cols"
  :class="['ske-'+item.type, {'ske-ani': active&&(item.type==='rect'||item.type==='circle')} ]"
  :style="{...item?.rowStyle,...item?.colStyle,...item?.style}">
  <item :paragraph="item" :active="active"></item>
</div>
</div>

Vue3 过10种组件通讯方式 - 掘金 (juejin.cn)Vue3 过10种组件通讯方式 - 掘金 (juejin.cn)

组课神器

function treeMenusRender(data, grade = 0) {
  grade++;
  let treeTemplate = "";
  data.forEach((item) => {
    treeTemplate += `
  <div class="tree-node" data-index="${item.id}" data-grade="${grade}">
  <div class="tree-node-content" style="margin-left: ${(grade - 1) * 15}px">
    <div class="tree-node-content-left">
      <img
        src="./images/dragger.svg"
        alt=""
        class="point-svg"
      />
      ${item.tag ? `<span class="tree-node-tag">${item.tag}</span>` : ""}
      <span class="tree-node-label">${item.label}</span>
    </div>
     ${
       item.tag
         ? `
     <div class="tree-node-content-right">
     <div class="students-count">
       <span class="number"> 0人完成</span>
       <span class="line">|</span>
       <span class="number">0人提交报告</span>
     </div>
     <div class="config">
       <img
         class="config-svg"
         src="./images/config.svg"
         alt=""
       />
       <button class="doc-link">编辑文档</button>
     </div>
   </div>`
         : ""
     }
  </div>
</div>
 ${item.children ? treeMenusRender(item.children, grade) : ""}
  `;
  });

  // TODO:根据传入的 treeData 的数据生成树型组件的模板字符串

  return treeTemplate;
}

您提供的两个正则表达式都可以用于匹配文本中形如 "第X章" 的章节信息,但它们的捕获行为略有不同。

  1. /第(\d)+章/g

    • :匹配文本中的 "第" 字符。
    • (\d)+:使用括号创建一个捕获分组,匹配一个或多个数字字符。括号内的 \d 是匹配任意数字字符的元字符,而 + 是匹配前面的元字符一次或多次。
    • :匹配文本中的 "章" 字符。
    • /g:全局匹配标记,表示在文本中查找所有匹配项。

    这个正则表达式会捕获每个匹配的数字字符,例如,在文本 "这是第3章的内容。这是第10章的内容。这是第20章的内容。" 中,它会匹配到 "3"、"10" 和 "20" 这三个数字。

  2. /第\d+章/g

    • :匹配文本中的 "第" 字符。
    • \d+:匹配一个或多个数字字符。
    • :匹配文本中的 "章" 字符。
    • /g:全局匹配标记,表示在文本中查找所有匹配项。

    这个正则表达式不使用捕获分组,它只会匹配整个 "第X章" 字符串。在相同的文本中,它会匹配到 "第3章"、"第10章" 和 "第20章" 这三个章节信息。

因此,如果您需要捕获章节信息中的数字部分,第一个正则表达式更合适;如果只需要匹配整个章节信息,第二个正则表达式更适用。

防抖和节流

function debounce(fn, delay) {
    let timer;
    return function (...args) {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
    };
}

function torele (fn,time){
    let start = 0
    return function () {
        let now = Date.now()
        if(now-start>time){
            // fn.apply(this,arguments)
            fn.call(this)
            start = now
        }

    }
}

神操作

this.currentIndex===1 &&(this.type=1) 这段代码是一个条件语句,它检查 this.currentIndex 是否等于 1,如果是,则将 this.type 设置为 1

这段代码通常在 Vue.js 或其他类似的前端框架中使用,其中 this 指向当前组件的实例。条件语句用于根据特定条件执行不同的操作。在这种情况下,如果 this.currentIndex 等于 1,则将 this.type 设置为 1

天气趋势


<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>天气趋势</title>
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
    />
    <link rel="stylesheet" type="text/css" href="css/style.css" />
    <script src="./js/axios.js"></script>
    <script src="js/vue.min.js" type="text/javascript" charset="utf-8"></script>
    <script
      src="js/echarts.min.js"
      type="text/javascript"
      charset="utf-8"
    ></script>
  </head>
  <body>
    <div id="app">
      <div class="top-bar">2022年 Y 城全年温度统计图</div>
      <!-- 主体 -->
      <div class="container">
        <!-- 月份 -->
        <div class="month">
          <ul>
            <!-- TODO:待补充代码 在下面的 li 标签中完成 12个月份 (即 monthList) 的渲染  -->
            <li :class="{'active':current===index}" @click="changeTab(index)" v-for="(item,key,index) in monthList" :key="index">{{item}}</li>
          </ul>
        </div>
        <div class="chart">
          <!-- TODO:待补充代码  -->
          <!-- currentMonth  未来七天和本月 tab 切换,只有当前月才显示 -->
          <div id="currentMonth" v-show="nowMonth===current">
            <div class="title">
              <h3>{{typeTitle}}</h3>
              <div class="type">
                <span id="seven" :class="{'active': isSevendays}" @click="handle7">未来7天</span>
                <span id="current" :class="{'active': !isSevendays}"  @click="handleMonth">本月</span>
              </div>
            </div>
          </div>
          <div id="chart"></div>
        </div>
      </div>
    </div>
  </body>
</html>
<script>
  // TODO:待补充代码
  var vm = new Vue({
    el: "#app",
    data: {
      chart: null, // 图表
      chartOptions: null, // 图表配置项
      typeTitle: "本月天气",
      current:0,
      isSevendays:false,
      data:[],
      nowMonth:(new Date()).getMonth(),
      monthdata: [
                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
                19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
              ],
      monthList: {
        January: "1月",
        February: "2月",
        March: "3月",
        April: "4月",
        May: "5月",
        June: "6月",
        July: "7月",
        August: "8月",
        September: "9月",
        October: "10月",
        November: "11月",
        December: "12月",
      },
    },
    mounted: function () {
      // 初始化 echarts
      axios.get("./js/weather.json").then(res=>{
        // console.log(res.data,Object.values(res.data[0]),res.data[0]);
        this.data = res.data
        this.changeTab(0)
      })
      this.$nextTick(() => {
        this.initChart();
      });
    },
    methods: {
      changeTab(e){
        if(e===this.nowMonth){
          this.isSevendays = false
        }
        this.current = e
        let xAxisData = Object.values(this.data[this.current])[0]
        this.chartOptions.xAxis[0].data = this.monthdata.slice(0,xAxisData.length)
        this.chartOptions.series[0].data = xAxisData
        console.log("lll",this.monthdata.slice(0,xAxisData.length));
        // console.log(xAxisData[0],this.chartOptions);
        this.chart.setOption(this.chartOptions);
      },
      handle7(){
        this.isSevendays  = true
        this.typeTitle = "未来七天天气";
        let xAxis = []
        let series = []
        let currentTime = new Date()
        // 月份是月份nowMonth
        for(let i =0;i<7;i++){
          let nextDate = new Date(currentTime.getTime()+i*24*60*60*1000)
          let month = nextDate.getMonth()
          let date = nextDate.getDate()
          let curMd = Object.values(this.data[month])[0]
          let formDate = month+1 +'/' + date
          xAxis.push(formDate)
          series.push(curMd[date-1])
        }
        console.log(xAxis);
        this.chartOptions.xAxis[0].data = xAxis
        this.chartOptions.series[0].data =series
        this.chart.setOption(this.chartOptions);
        
      },

      handleMonth(){
        this.typeTitle = "本月天气";
        this.isSevendays  = false
        this.changeTab(this.nowMonth)
      },

</script>

image.png

image.png

(val,key,index) in monthList 遍历对象 在 Vue.js 中,v-for 指令可以用于遍历对象的属性。你可以像下面这样使用 v-for 来遍历对象:

<div v-for="(val, key, index) in monthList" :key="index">
  {{ key }}: {{ val }}
</div>

在这个例子中,假设 monthList 是一个对象,val 表示对象的值,key 表示对象的键,index 表示当前项的索引。在 v-for 中,valkeyindex 是由 Vue.js 提供的特殊变量,用于在遍历过程中访问对象的属性和索引。

在模板中,可以使用这些变量来显示对象的键和值。在这个例子中,每次循环,都会创建一个 <div> 元素来显示对象的键和值。:key="index" 是为了帮助 Vue.js 识别每个循环项的唯一性。

image.png