KenOscar

KenOscar 查看完整档案

青岛编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

Hello world

个人动态

KenOscar 提出了问题 · 10月14日

react 无状态组件,子组件如何修改父组件传过来的值

react无状态组件 无state

父组件调用 子组件弹窗

const [editVisible, setEdit] = useState(false);
 //修改
    const edit = (i) => {
        setEdit(true)
    }
<Button onClick={() => edit(i)}>修改</Button>
<Edit ishow={editVisible}></Edit>

子组件 想要在自身的组件里面修改这状态 关闭本身 应该如何修改

export default function Edit(prop) {
    console.log(prop.ishow)
    //声明form
    const [form] = Form.useForm();
    const [visible, setVisible] = useState(false);
    const [sortVisible, setSort] = useState(false);
    const [editVisible, setEdit] = useState(false);
    const [exam, setExam] = useState([]);
    const [name, setName] = useState("");
    useEffect(() => {
        console.log(123);
    }, [])
    return (
        <div>
            {/* 修改*/}
            <Modal
                title="修改"
                visible={prop.ishow}
                okText="确认"
                cancelText="取消"
                onOk={() => { 这里应该怎么写 }}
                onCancel={() => { 这里应该怎么关闭; form.resetFields(); }}
            >
                <Form form={form}>
                    <Form.Item
                        label="考试名称"
                        name="name"
                        rules={[
                            {
                                required: true,
                                message: '请输入考试名称',
                            },
                        ]}
                    >
                        <Input onChange={(e) => { setName(e.target.value) }} />
                    </Form.Item>

                </Form>
            </Modal>
        </div>
    );
}

现在只能打开 但是无法关闭 因为不知道在子组件里面如何修改这个prop.ishow

关注 4 回答 4

KenOscar 赞了回答 · 10月13日

解决菜单刷新之后高亮不在此menu,怎么样才能让它在页面刷新之后,menu的高亮样式还是这个menu

不需要用vuex和storage,使用route也可以直接实现。
html代码:

<a-menu
    theme="dark"
    :openKeys="openKeys"
    @openChange="onOpenChange"
    mode="inline"
    :defaultSelectedKeys="defaultSelectedKeys"
  >
    <template v-for="item in menuList">
      <a-menu-item v-if="item.children === false" :key="item.router">
        <a-icon :type="item.icon" />
        <span>{{ item.name }}</span>
      </a-menu-item>

      <a-sub-menu v-else :key="item.router">
        <span slot="title"
          ><a-icon :type="item.icon" /><span>{{item.name}}</span></span
        >
        <a-menu-item v-for="menuChildren in item.children" :key="menuChildren.router">
          <router-link :to="menuChildren.router">{{menuChildren.name}}</router-link>
        </a-menu-item>
      </a-sub-menu>
    </template>
  </a-menu>

js代码:

data() {
    return {
      defaultSelectedKeys: [this.$route.path],
      openKeys: [""],
      menuList,
    };
},
created() {
    const result =  this.menuList;
    this.menuList = result; 
    this.showMenu(result);
},
methods: {
    showMenu(result) {
      for (let i = 0; i < this.menuList.length; i++) { 
        if (result[i].children !== false) {  
          for (let y = 0; y < result[i].children.length; y++) {  
            if (result[i].children[y].router === this.$route.path) { 
              this.openKeys = [result[i].router]
            }
          }
        }
      }
    },
    onOpenChange(openKeys) {
      if (openKeys.length !== 0) {
        this.openKeys = [openKeys[1]];
      } else {
        this.openKeys = [""];
      }
    },
    clickItem(obj) {
      this.$router.push(obj.key);
    },
},

列表数据:

// 此处为动态获取的菜单数组
const menuList = [
  {
    name: "首页",
    router: "/home",
    icon: "database",
    children: false,
  },
  {
    name: "总览",
    router: "/",
    icon: "appstore",
    children: [
      {
        name: "实时数据总览",
        router: "/",
      },
      {
        name: "历史数据总览",
        router: "/historyCheckOV",
      },
    ],
  },
  {
    name: "场站",
    router: "/stationRealTime",
    icon: "database",
    children: [
      {
        name: "场站实时数据",
        router: "/stationRealTime",
      },
      {
        name: "场站历史数据",
        router: "/stationHistory",
      },
      {
        name: "场站数据异常",
        router: "/stationAbnormal",
      },
    ],
  },
  {
    name: "母线",
    router: "/busRealTime",
    icon: "branches",
    children: [
      {
        name: "母线实时数据",
        router: "/busRealTime",
      },
      {
        name: "母线历史数据",
        router: "/busHistory",
      },
      {
        name: "母线数据校核",
        router: "/busDataCheck",
      },
    ],
  },
  {
    name: "发电机",
    router: "/PSrealTime",
    icon: "interaction",
    children: [
      {
        name: "发电机实时数据",
        router: "/PSrealTime",
      },
      {
        name: "发电机历史数据",
        router: "/PShistrory",
      },
      {
        name: "发电机数据校核",
        router: "/PSdataCheck",
      },
    ],
  },
  {
    name: "线路",
    router: "1",
    icon: "pull-request",
    children: [],
  },
  {
    name: "变压器",
    router: "2",
    icon: "table",
    children: [],
  },
  {
    name: "无功补偿装置",
    router: "3",
    icon: "database",
    children: [],
  },
  {
    name: "质量标注",
    router: "4",
    icon: "file-done",
    children: [],
  },
];

关注 3 回答 3

KenOscar 回答了问题 · 10月13日

解决react子组件怎么改变父组件的state

纯函数组件 子组件如何修改父组件传来的值?

关注 14 回答 8

KenOscar 赞了回答 · 10月13日

react利用ant 的Table实现单行的删除时候为什么Popconfirm的onConfirm的回调传入参数会自动执行?

onConfirm = {() => this.onDelete(key)}

关注 2 回答 2

KenOscar 赞了回答 · 10月12日

解决react纯函数组件,父组件异步获取数据传递给子组件的问题(子组件拿到的数据是空)

你在父组件中直接定义了变量,不是通过useState来定义。

const [exam, setExam] = useState([]);
    useEffect(() => {
        axios.post(`url`,{}).then((res) => {
            if (res.success){
               setExam(res.result);
                console.log(22222,exam)
            }else{
                message.error(res.message)
            }
        })
    }, [])

关注 2 回答 1

KenOscar 回答了问题 · 10月12日

elementui每次上传一个文件,文件覆盖的时候,显示的文件名不变?

file-list里面的name修改一下

关注 4 回答 2

KenOscar 提出了问题 · 10月12日

解决react纯函数组件,父组件异步获取数据传递给子组件的问题(子组件拿到的数据是空)

纯函数组件没有生命周期

父组件

import React, { useState, useEffect } from 'react';
import { Button, message } from 'antd';
import List from '../../components/List/List'
import './Exam.scss'
import axios from '../../service/axios';

export default function Exam() {
    let exam =[];
    useEffect(() => {
        axios.post(`url`,{}).then((res) => {
            if (res.success){
                exam = res.result;
                console.log(22222,exam)
            }else{
                message.error(res.message)
            }
        })
    }, [])
    return (
        <div className="test">
            <List list={exam}></List>
        </div>
    );
}

子组件

import React, { useState,useEffect } from 'react';
import { Image } from 'antd';
import { useHistory } from 'react-router-dom';
import "./List.scss";

function ListItem(props) {
    const hisorty = useHistory();
    const i = props.value;
    const handleClick = (i) => {
        hisorty.push(i.path)
    };
    return (
        <div className="block" onClick={() => handleClick(i)}>
            ![]({i.url} style={{ height: )
        </div>
    )
}
export default function List(props) {
//这里获得的props一直是空 如果父组件的数据是写死的 传递过来 就可以拿到 但是如果是axios拿来的 就是空 怀疑是异步的问题 
这种场景下 如何拿到父组件数据
     useEffect(() => {
       console.log(1111,props)
    }, [])
    const numbers = props.list;
    console.log(numbers)
    const listItems = numbers.map((item, index) =>
        <ListItem key={index} value={item} />

    );
    return (
        <ul>
            {listItems}
        </ul>
    );
}

关注 2 回答 1

KenOscar 回答了问题 · 10月10日

在react代码中的axios拦截器中做链接的转跳

问一下 react项目中 拦截器要怎么配置

单独配置一个js文件 然后引入到index.js里面吗

关注 4 回答 4

KenOscar 赞了文章 · 10月9日

「React」如何在React中优雅的实现动画

有没有内推的,求个机会,QQ: 1025873823

最简单的动画组件实现

动画的本质,无非就是一个状态样式到另一个状态样式的过渡。最简单的动画组件,我们只需要指定两个状态的样式(进入的样式,离开的样式),以及一个开关(控制状态),即可完成。

animate.png

codepen地址

animate.gif

实现一组动画的过渡

实现一组动画的过渡。我们只需要在多个最简单的动画组件的基础之上,设置一个统一的开关,统一控制,多个动画组件动画的状态即可。如果想实现有交错的过渡(有时间间隔的过渡),我们只需要根据动画组件在一组元素中的索引位置,设置合适的延迟即可。

为了引入统一的开关的控制,我们为动画组件添加一个父级组件,父级组件的开关控制所有子组件的开关状态。父组件使用React.Context将自己开关的状态,下发给子组件。

为了实现交错效果,我们需要为列表中子组件设置不同长度的延迟。延迟时长和子组件在列表索引,以及开关的状态有关。

比如:

在开关设置为true, 需要显示入场的动画。延迟自上而下,依次增大(0ms, 100ms, 200ms, 300ms)

在开关设置为false, 需要显示出场的动画。延迟自上而下,依次减小(300ms, 200ms, 100ms, 0ms)

animates1.png

animates2.png

codepen地址

animates.gif

上述组件目前存在的问题

  1. 节点必须事先已经渲染好,对于动态插入的节点,这些动画组件无能为力。(在下方我们参考了`react-transition-group实现解决这个问题)
  2. 如果元素起始样式有display: none动画将不会起效果(这个问题其实和动态插入节点属于一类问题)。
  3. 对于一组列表节点。新的节点的插入,和删除时。其他节点的过渡很生硬,没有动画效果(我们可以使用flip动画解决这个问题)。

FLIP动画

FLIP动画实现原理是: 缓存元素起点的位置, 然后将元素置于终点的位置,计算终点与起点的差值,根据差值应用动画。

我们先看看flip动画强大的效果

效果1.gif

效果2.gif

接下来,我们来一步一步实现一个简易的flip动画,然后再尝试在react中实现。

闪烁

请问下面的代码,会造成闪烁的问题吗?

flip1.png

codepen演示

答案: 是不会。具体原因和浏览器的事件队列有关。点击事件的代码,我们必须执行完成当前的任务(当前代码段的执行)才会进行浏览器渲染。

这一点对我们很重要。再重申一遍flip动画的原理,缓存元素起点的位置, 然后将元素置于终点的位置,计算终点与起点的差值,根据差值应用动画。

雏形

然后基于上面的代码,我们目前可以实现一个简易的flip动画

flip2.png

codepen演示

我们可以看到,动画已经实现,但是目前动画的计算还是固定的,我们接下来尝试让它自动化。

fli3.gif

完善

我们尝试对, 之前状态和当前状态的属性,做自动的差值计算。

flip3.png

codepen演示

我们目前已经实现了,宽度和x轴的flip动画。

flip4.gif

🤔️为什么要这样计算(之前的位置 - 现在的位置)?

我们为什么要使用之前的样式值减去当前的样式值?

FLIP动画的原理是基于当前位置和起始位置的动画,我们在做动画的时候,元素其实已经到达了结束的位置。

比如当前的位置是100px, 开始位置是0px。flip动画需要模拟从0px到100px的过程,但是当前位置已经是100px了,所以我们必须使用 translateX(0 - 100px), 模拟动画开始时的0px的位置。

100ms

There is a window of 100ms after someone interacts with your site where you’re able to do work without them noticing.

使用flip动画时,切记计算不能超过100ms,如果超过100ms用户会感到卡顿。

100ms.jpg

FLIP 与 Web Animations API

目前距离实现一个真正的flip动画库还有不少的距离。继续使用 requestAnimationFrame 会很困难,太复杂了。

既然flip动画,是基于结束位置和开始位置的动画,那么有没有什么好办法,不需要我们手动的去调整。只需要提供初始位置和结束位置完成动画呢?我们可以使用Web Animations API。

对于 Web Animations API本身,我在这里不想做过多的介绍。大家只需要知道,使用Web Animations API后,我们只需要设置开始的样式,和结束的样式,动画就会自动完成。

我们将上面的demo,改造成使用Web Animations API的形式。

flip5.png

codepen演示

flip6.gif

可以看到,我们在代码里只需要设置开始和结束的样式,动画就会自动过渡完成。

React 与 FLIP

如何在react中完成flip动画呢?我们首先回忆下在js中flip动画的逻辑

  1. 缓存元素起始位置
  2. 将元素移动到结束的位置
  3. 获取当前的位置,并计算当前的位置与缓存的起始位置的差值。
  4. 下一帧开始时,开始做动画

我们可以发现,第1,2,3步都是发生在渲染到屏幕之前(或者说渲染到屏幕的那一刻)。那么在react中,有什么hook发生在渲染到页面的那一刻呢?答案是: 函数组件中是useLayoutEffect。class组件中是componentDidUpdate

我们整理下在react中flip动画的实现逻辑

  1. 在页面第一次useEffect, 元素渲染完成。这时同时缓存元素的位置。
  2. state发生变化,组件需要重新渲染
  3. 在组件重新渲染到屏幕那一刻,在useLayoutEffect中,我们获取最新的位置。并计算当前的位置与缓存的起始位置的差值。
  4. 动画开始执行

那么接下来我们来实现一个react中flip的雏形

flip7.png

codepen演示

flip8.gif

bigo!我们成功在react中实现了flip动画

❓ 目前存在的问题

如果我们在flip动画运行过程中,切换动画。动画会出现闪烁,我们现在来着手解决这个问题。

我们先来思考一下这个问题产生的原因。动画在运行过程中,还没有到达终点,这时切换动画,动画元素会被强行移动到终点的位置,然后进行下一次动画,这就是动画闪烁的原因。

如何解决呢?

  1. 在切换动画的时候,如果上一次动画没有结束,我们手动将其结束
  2. 在切换动画的时候,更新位置的缓存。

flip8.png

codepen演示

flip9.gif

虽然目前已经实现flip动画的效果,但是距离封装成可用的库还有些距离,如果大家想要了解的更多,可以查看我封装好的源码(原理和上面的文章是一模一样的)。仓库地址: https://github.com/peoplesing...

🚧 Flip动画需要注意的点

  1. flip计算动画位置时,元素上最好不要有transition的css属性,会影响到位置的计算。
  2. 之前的计算缓存位置时,都是相对于body的位置。但是如果存在有滚动条时,缓存的位置会有问题。解决办法是,基于动画元素的父级元素计算位置,而不是body的位置。

Flip如何实现交错效果?

好吧。目前我的库中,交错效果的完善解决方案还没有实现。但是主体思路是有了,并有了简易的实现版本 见下方👇

效果3.gif

🔍🔍🔍 动态插入节点的动画处理

这个问题解决的思路,我参考了 react-transition-group库 的源码。在这里我说一下,react-transition-group库 实现的思路。


<react-transition-group>
  {
    list && list.map((item) => (
      <react-transition>
        { item }
      </react-transition>
    ))
  }
<react-transition-group>

最外层的 <react-transition-group> 组件 并不会直接对嵌入的children进行直接渲染。而是将props.children保存为,组件的内部状态state。这样我们可以在children渲染之前,对state做一些额外的操作。

<react-transition-group>会对于动态插入的节点,不会直接渲染。而是先将,新插入节点外层的<react-transition>组件的动画状态设置为'Leave'态(这里处理的目的是,即使dom渲染完成后,元素也是隐藏的状态)。然后在<react-transition>中,会先等待dom渲染完成,然后再将动画的状态设置为'Entering',完成'Leave'态到'Entering'态的动画过渡。

<react-transition-group>会对于动态删除的节点,不会直接删除。而是先将需要删除节点外层的<react-transition>组件的动画开关设置为false,动画开始向'Leave'态过渡。动画过渡完成后,然后会触发<react-transition>组件的 onLeave 事件。在 onLeave 事件中会删除dom节点。

总结一下 react-transition-group 库的处理方式:

  1. 插入的节点,先渲染dom,然后再做动画
  2. 删除的节点,先做动画,然后再删除dom

写在最后

如果您对我的文章感到满意,还请麻烦您给我的文章点一个赞。如果您喜欢我的小项目,还请帮我的小项目点一个star。谢谢🙏

项目地址:https://github.com/peoplesing...

参考

查看原文

赞 18 收藏 9 评论 3

KenOscar 赞了文章 · 10月9日

《前端每日实战》第174号作品:日历

image

中秋国庆长假休完,又要投入到工作中了,做一个日历纪念一下吧。

效果预览

按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。

https://codepen.io/comehope/pen/mdEyWEv

源代码下载

每日前端实战系列的全部源代码请从 github 下载:

https://github.com/comehope/front-end-daily-challenges

代码解读

这个日历的开发流程是,定义 DOM 结构之后,进行页面整体布局,绘制出日历薄的模样,然后分别布局上部的当前日期和下部的日期表格。完成静态布局之后,再通过脚本来动态生成日期元素,实现一个显示实时日期的动态日历。

一、定义 DOM 结构

dom 的整体结构是一个名为 .container 的容器中包含2个元素,.today 是当前日期,.calendar 是日期表格。

<div class="container">
    <header class="today">
    </header>
    <main class="calendar">
    </main>
</div>

.today 当前日期部分包含2个元素,.month 显示当前月,.day显示当前日。

<header class="today">
    <div class="day">9</div>
    <div class="month">October</div>
</header>

.calcendar 日期表格部分包含一个表头行 .days 和一个表格 .dates。表头按英文习惯以周日为每周的第一天;表格共6行,一共显示42天,可以适应任何月份。
表格中的日期通过类名区分为上月日期 previous-month、当前日期 current-day、下月日期 next-month

<main class="calendar">
    <div class="days">
        <span>Sun</span>
        <span>Mon</span>
        <span>Tue</span>
        <span>Wed</span>
        <span>Thu</span>
        <span>Fri</span>
        <span>Sat</span>
    </div>
    <div class="dates">
        <span class="previous-month">27</span>
        <span class="previous-month">28</span>
        <span class="previous-month">29</span>
        <span class="previous-month">30</span>
        <span>1</span>
        <span>2</span>
        <span>3</span>
        <span>4</span>
        <span>5</span>
        <span>6</span>
        <span>7</span>
        <span>8</span>
        <span class="current-day">9</span>
        <span>10</span>
        <span>11</span>
        <span>12</span>
        <span>13</span>
        <span>14</span>
        <span>15</span>
        <span>16</span>
        <span>17</span>
        <span>18</span>
        <span>19</span>
        <span>20</span>
        <span>21</span>
        <span>22</span>
        <span>23</span>
        <span>24</span>
        <span>25</span>
        <span>26</span>
        <span>27</span>
        <span>28</span>
        <span>29</span>
        <span>30</span>
        <span>31</span>
        <span class="next-month">1</span>
        <span class="next-month">2</span>
        <span class="next-month">3</span>
        <span class="next-month">4</span>
        <span class="next-month">5</span>
        <span class="next-month">6</span>
        <span class="next-month">7</span>
    </div>
</main>

二、页面整体和日历容器布局

先用线性渐变设置页面背景色为灰白过渡色。

body {
    margin: 0;
    height: 100vh;
    background-image: linear-gradient(to bottom, #eee, #ccc);
}

设置容器尺寸,用相对单位 em,并使容器居于页面正中。
为使容器可见,暂为容器填充白色背景。

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

.container {
    width: 32em;
    height: 38em;
    font-size: 14px;
    background-color: white;
}

效果如下图:
image

注释掉刚才临时定义的 background-color 属性,改为用锐利渐变填充,实现上部黄棕色、下部白色的效果。因为黄棕色 sandybrown 是日历主色,后面还会用到,所以把它定义成变量。
再把日历四周设为圆角,底部加双层阴影,模拟多张日历纸叠加的效果。

.container {
    /* background-color: white; */
    --main-color: sandybrown;
    background-image: linear-gradient(to bottom, var(--main-color) 50%, white 50%);
    border-radius: 1em;
    box-shadow: 
        0 2px 1px rgba(0, 0, 0, 0.2),
        0 3px 1px white;
}

效果如下图:
image

接下来绘制一个细节:环扣,它用来连接日历的上、下两部分。使用2个伪元素来绘制,这样不用显式地增加 DOM 元素,纯用 CSS 实现装饰效果。两个环扣的样式相同,所以大部分代码是共享的,仅它们所处的位置不同,一个在日历左侧,一个在日历右侧。

.container {
    position: relative;
}

.container::before,
.container::after {
    content: '';
    position: absolute;
    width: 0.6em;
    height: 2.3em;
    background-color: white;
    top: calc(50% - 2.3em / 2);
    border-radius: 0.3em;
    box-shadow: 
        0 3px 1px rgba(0, 0, 0, 0.3),
        0 -1px 1px rgba(0, 0, 0, 0.2);
}

.container::before {left: 2em;}
.container::after {right: 2em;}

效果如下图:
image

至此,一个接近真实场景中的日历的轮廓绘制完成了,接下来布局日历上显示的文字内容。

三、上部当前日期布局

因为整个日历分成上下两部分,所以先令当前日期 .today 占据上部的50%空间,这样表格 .calendar 自然就被挤到下部了。

.today {
    height: 50%;
}

效果如下图:
image

因为整个日历都使用同一种字体,所以把字体样式定义在容器中,采用无衬线字体。
.today 的布局很简单,用的都是字号、颜色、行间距等基本属性。

.container {
    font-family: sans-serif;
}

.today {
    padding: 3em;
    box-sizing: border-box;
    color: white;
}

.today .day {
    font-size: 8em;
    line-height: 1em;
    font-weight: bold;
}

.today .month {
    font-size: 4em;
    line-height: 1em;
    text-transform: lowercase;
}

效果如下图:
image

四、下部日期表格布局

表格包括表头和表体两部分,设置好它们的宽度,然后用 flex 布局令其垂直居中排列。

.calendar {
    padding-top: 3.5em;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.calendar .days,
.calendar .dates {
    width: 28em;
}

表头和表格都是一行7列,这里采用 grid 布局实现。

.calendar .days,
.calendar .dates {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    line-height: 2em;
    text-align: center;
}

效果如下图:
image

表格已经成形,剩下的细节是为文字上色。
表格里一共有5种语义元素:表头、当前日期、本月日期、上月日期、下月日期,这些不同语义的元素都靠 CSS 类名来区分。表头和当前日期用主色,本月日期用深灰色,上月日期和下月日期用浅灰色。

.calendar .days {
    color: var(--main-color);
    font-weight: bold;
    text-transform: uppercase;
}

.calendar .dates {
    color: dimgray;
}

.calendar .dates .previous-month,
.calendar .dates .next-month {
    color: lightgray;
}

.calendar .dates .current-day {
    color: var(--main-color);
    font-weight: bold;
}

效果如下图:
image

最后,再增加一个鼠标悬停特效,当在日期上悬停时令背景变灰、文字变白,并用 transition 实现平滑过渡。

.calendar .dates span:hover {
    background-color: lightgray;
    color: white;
}

.calendar .dates span {
    transition: 0.3s;
}

至此,整个日历的静态布局全部完成了。

五、动态脚本

程序的入口是一个名为 init 的函数,其中声明了一个 Calendar 类的实例,再调用它的 render() 方法来渲染页面。

function init() {
    let calendar = new Calendar(new Date())
    calendar.render()
}

window.onload = init

Calendar 类接收一个日期参数,以此来初始化年、月、日数据。render() 方法分别调用了 renderDay()renderMonth() 来渲染当前日期和当前月份。因为 Date 对象返回的月份属性是数字,为了把它显示成英文月份名称,就定义了一个存放月份名称的数组。

let Calendar = function(date) {
    let year = date.getFullYear()
    let month = date.getMonth()
    let day = date.getDate()

    function renderDay() {
        document.querySelector('.day').textContent = day
    }

    function renderMonth() {
        const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
        document.querySelector('.month').textContent = MONTHS[month]
    }

    this.render = function() {
        renderDay()
        renderMonth()
    }
}

接下来要渲染日期表格了。
我们先引入一个日历库 calendar-dates(github 地址:https://dance2die.github.io/calendar-dates/),它负责计算日期、星期、月份之间的关系,为给定的年月输出对应的日历数据。通过 import 语句导入该库文件。

import CalendarDates from 'https://cdn.jsdelivr.net/npm/calendar-dates@2.6.1/dist/calendardates.esm.js'

image

注意,对于使用了 import 语句的脚本,在 <script> 标签中需增加 type="module" 属性。

<script data-original="script.js" type="module"></script>

然后,在 Calendar 类中定义一个 renderDates() 函数来渲染日历列表。如何获得日历可以参考官方文档,这里就不多说了,我觉得有点别扭的是必须用 async/await 的方式来调用。每个日期有 datetype 属性,date 就是日期数值,type 有3个值:previouscurrentnext,分别代表上月、本月、下月,我们就用这2个属性来处理日期元素的样式。
最后,别忘了要在 render() 里调用一下 renderDates() 函数。

async function renderDates() {
    const calendarDates = new CalendarDates();
    const domList = document.querySelector('.dates')
    domList.innerHTML = ''
    for (const meta of await calendarDates.getDates(new Date(year, month))) {
        let span = document.createElement('span')
        span.textContent = meta.date
        span.className = (meta.type == 'current')
            ? (meta.date == day)
                ? 'current-day'
                : ''
            : meta.type + '-month'
        domList.append(span)
    }
}

this.render = function() {
    renderDay()
    renderMonth()
    renderDates()
}

大功告成!

关于我

张偶,网络笔名 @comehope,20世纪末触网,被 Web 的无穷魅力所俘获,自此始终战斗在 Web 开发第一线。

《前端每日实战》专栏是我近年来实践项目式学习方法的笔记,以项目驱动学习,展现从灵感闪现到代码实现的完整过程,亦可作为前端开发的练手习题和开发参考。

拙作《CSS3 艺术》一书于2020年1月由人民邮电出版社出版,全彩印刷,使用100多个生动美观的实例,系统地剖析了 CSS 与视觉效果相关的重要语法,并含有近10小时的视频演示讲解。京东/天猫/当当有售。

查看原文

赞 23 收藏 9 评论 1

认证与成就

  • 获得 24 次点赞
  • 获得 312 枚徽章 获得 6 枚金徽章, 获得 80 枚银徽章, 获得 226 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-03-29
个人主页被 2.5k 人浏览