整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Jest直通车- react表单上传

明:本篇测试是react+jest+@testing-library+react-router-dom+react-redux,如果react有疑问的,可以google,资源Supernumerary

我们已经讲完了表单内容,那么现场来一个案例试试水~

表单上传

这里完成的任务主要是将以上所讲内容进行整合,同时发起一次api请求。

首先我们在express/index.js中加入一个post请求:

...
import bodyparser from 'body-parser';
// 解析body
app.use(bodyparser.urlencoded({extended:false}));

app.post('/home',(req,res) => {
  const body = req.body;
  console.log(body);
  res.send({
    status: 0,
    msg: 'submit success'
  })
})

如果您不知道为啥这里突然加了一个借口,建议回顾一下我当时项目搭建的过程:
https://www.toutiao.com/article/7250691387551826432/

接着新建网络请求api/detail.ts文件

import axios from "axios";
const commonUrl = '/api';
interface Obj {
    username: string,
    age: string,
    email: string,
    city: string,
    like: string,
    isLike: boolean
}

export const submitData = async (data: Obj) => {
    const path = commonUrl + '/home';
    const res = await axios.post<any>(
        path,
        {...data},
        {
            headers: {
                'Content-Type':  'application/x-www-form-urlencoded'
            }
        }
    );
    return res.data
}

然后我们写一个组件page/detail/Com/submit.tsx

import React,{useEffect,useState} from "react";
import {
    Grid,
    styled,
    Box,
    MenuItem,
    Button
} from '@mui/material';

import ZlInput from "src/component/common/Input";
import RadioButtonsGroup from "src/component/common/radioGroup";
import Zl_checkbox from "src/component/common/checkbox";

import { submitData } from "../../../api/detail";

const ItemStyle = styled('div')({
    [`& .MuiFormControl-root `]: {
        verticalAlign: 'bottom',
        marginLeft: '20px'
    },
    [`& span`]: {
        display: 'inline-block',
        width: '80px',
        textAlign: 'right'
    },
    width: '100%'
})

const RadioStyle = styled('div')({
    display: 'flex',
    alignItems: 'center',
    [`& .MuiFormControl-root `]: {
        verticalAlign: 'bottom',
        marginLeft: '20px'
    },
    [`& .radio-left`]: {
        width: '80px',
        textAlign: 'right',
    },
})
const LikeStyle = styled('div')({
    display: 'flex',
    alignItems: 'center',
    marginTop: '-10px',
    [`& .MuiButtonBase-root`]: {
        marginLeft: '6px'
    },
    [`& .radio-left`]: {
        width: '80px',
        textAlign: 'right',
    },
})
const CityStyle = styled('div')({
    [`& .MuiFormControl-root `]: {
        verticalAlign: 'bottom',
        marginLeft: '20px'
    },
    [`& span`]: {
        display: 'inline-block',
        width: '80px',
        height: '42px',
        textAlign: 'right'
    },
    width: '100%'
})
const SubmitStyle = styled('div')({
    [`& .MuiButtonBase-root `]: {
        verticalAlign: 'bottom',
        marginLeft: '20px'
    },
    [`& span`]: {
        display: 'inline-block',
        width: '80px',
        height: '42px',
        textAlign: 'right'
    },
    width: '100%'
})
interface Item {
    value: string,
    label: string
}
interface Obj {
    username: string,
    age: string,
    email: string,
    city: string,
    like: string,
    isLike: boolean
}

const dd = [
    { value: 'apple', label: 'apple'},
    { value: 'apple2', label: 'apple2'},
    { value: 'apple3', label: 'apple3'},
]

const currencies = [
    {
      value: 'beijing',
      label: 'beijing',
    },
    {
      value: 'shanghai',
      label: 'shanghai',
    },
    {
      value: 'shenzhen',
      label: 'shenzhen',
    }
];

export default function Submit() {
    const [obj,setObj] = useState<Obj>({
        username: '',
        age: '',
        email: '',
        city: '',
        like: '',
        isLike: false
    })
    const [like,setLike] = useState('');
    

    const handleLikeOption = (event: React.ChangeEvent<HTMLInputElement>) => {
        // console.log(event.target.value,'like');
        const target = event.target;
        if ( target.checked ) {
            setLike(event.target.value);
            setObj({
                ...obj,
                like: event.target.value
            })
        } else {
            setLike('')
        }
        
    }

    const City = () => {
        return (
            <ZlInput id="city" data-testid='city' inputType='select' label='city' handleInputChange={changeCity} placeholder="please input" defaultValue={currencies[0].value} variant="standard" helperText="Please select your city" sx={{ width: '400px'}}>
                {currencies.map((option) => (
                    <MenuItem key={option.value} value={option.value}>
                        {option.label}
                    </MenuItem>
                ))}
            </ZlInput>
        )
    }

    const changeCity = (event: React.ChangeEvent<HTMLInputElement>) => {
        setObj({
            ...obj,
            city: event.target.value
        })
    }

    const handleChangeLike = (event: React.ChangeEvent<HTMLInputElement>) => {
        setObj({
            ...obj,
            isLike: event.target.checked
        })
    }


    const handleSubmit = async () => {
        console.log('--submit data--',obj)
        const res = await submitData(obj)
        console.log(res,'submit')
    }

    const changeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        const val = e.target.value;
        if ( e.target.labels?.length ) {
            // 这里有一个BUG,明明给了age输入框一个label,但是labels获取的NodeList为空数组,其他的却正常
            const labelValue = e.target?.labels[0]?.textContent;
            switch(labelValue) {
                case 'username':
                    setObj({
                        ...obj,
                        username: val
                    })
                    break;
                case 'age':
                    setObj({
                        ...obj,
                        age: val
                    })
                    break;
                case 'email':
                    setObj({
                        ...obj,
                        email: val
                    })
            }
        } else {
            // console.log(e.target.labels,'kkk')
            setObj({
                ...obj,
                age: val
            })
        }
    }
    return (
        <Box sx={{ display: 'flex', flexWrap: 'wrap', width: '800px' }}>
            <Grid container spacing={2} sx={{ padding: '20px'}}>
                <Grid item xs={12}>
                    <ItemStyle>
                        <span>username: </span><ZlInput label='username' data-testid='username'  handleInputChange={changeChange} defaultValue={obj.username} id="usename"  placeholder="please input" variant="standard" sx={{ width: '400px'}}  />
                    </ItemStyle>
                </Grid>
                
                <Grid item xs={12}>
                    <ItemStyle>
                        <span>age: </span><ZlInput id="age" label='age' data-testid='age'  handleInputChange={changeChange} defaultValue={obj.age}  placeholder="please input" variant="standard" sx={{ width: '400px'}} />
                    </ItemStyle>
                </Grid>
                <Grid item xs={12}>
                    <ItemStyle>
                        <span>email: </span><ZlInput id="email" data-testid='email'  handleInputChange={changeChange} label='email' defaultValue={obj.email} placeholder="please input" variant="standard" sx={{ width: '400px'}} />
                    </ItemStyle>
                </Grid>
                <Grid item xs={12}>
                    <CityStyle>
                        <span>city: </span><City />
                    </CityStyle>
                </Grid>

                <Grid item xs={12}>
                    <RadioStyle>
                        <div className="radio-left">like: </div><RadioButtonsGroup data-testid='likeOptions'  id="like-btn" dd={dd}  handleChange={handleLikeOption} value={like} />
                    </RadioStyle>
                </Grid>
                <Grid item xs={12}>
                    <LikeStyle>
                        <div className="radio-left">like apple: </div><Zl_checkbox 
                            checked={obj.isLike}
                            handleChange={handleChangeLike}
                            data-testid='like_apple'
                        />
                    </LikeStyle>
                </Grid>
                <Grid item xs={12}>
                    <SubmitStyle>
                        <span></span>
                        <Button variant="contained" data-testid='submit' color="success" onClick={handleSubmit}>Submit</Button>
                    </SubmitStyle>
                </Grid>
            </Grid>
        </Box>
    )
}

看起来写了很长,但是仔细阅读,无非就是一些表单子内容,我们这里用到的是mui,可以赋值跑通,结果大概长这个样子:

页面展示效果

最后我们就可以写测试文件__tests__/react/com/submit.test.tsx

import { fireEvent, screen } from '@testing-library/dom'
import { render, waitFor } from "@testing-library/react";

import * as utils from '../../../api/detail';
jest.mock('../../../api/detail');

const submitDD = jest.spyOn(utils,'submitData');
submitDD.mockImplementation(
    (): Promise<any> => {
        return new Promise((resolve) => {
            resolve({
                status: 0,
                msg: 'submit success2'
            })
        })
    }
)

// 引入jest-dom的匹配内容
import '@testing-library/jest-dom';
import { 
    setRadio,
    getSelectValue,
    InputField,
    changeCheckbox  
} from '../utils';

import Submit from 'src/page/detail/Com/submit';

describe('test react submit', () => {
    it('test input',async () => {
        console.log('kkk')
        const Com = (
            <Submit />
        )
        const container = render(Com);

        // 三个输入框
        InputField({
            testId: 'username',
            value: 'jack'
        })
        InputField({
            testId: 'age',
            value: '12'
        })
        InputField({
            testId: 'email',
            value: '111@qq.com'
        })

        // 选择城市
        getSelectValue({
            testId: 'city',
            value: 'shanghai'
        })

        // 选择喜欢项
        const likeValue = setRadio({
            value: 'apple2'
        })

        // 确定是否要apple
        const checked2 = changeCheckbox({
            testId: 'like_apple'
        })

        // 提交
        const submitBtn = screen.getByTestId('submit');
        fireEvent.click(submitBtn);

        
        await waitFor(() => {
            expect(submitDD).toHaveBeenCalled();
        })
        
        expect(container).toMatchSnapshot('submit-input')        
    })
})

当我们成功跑通的时候,我们就会看到如下的打印

表明模拟了表单提交

注意事项:

  • express注意解析body,引入body-parser
  • axios发起post请求时,我们携带的data如果想被express解析,那么需要加一个头:headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
  • 注意我这里是新建了一个detail.ts来写detail页面的网络请求,那么为什么我不直接写在home.ts里面呢?原因是我测试的时候,发现如果我写在homt.ts中时,可能由于之前我们使用了自定义mock,home.ts已经被__mocks__/home.ts所管理,导致页面无法访问post请求的这个方法

组件详谈

表单组件基本元素为这些了,那么对于列表list以及表格这块怎么说呢,其实这两个我们只需要给个初始值,他能够正常渲染,覆盖率基本上就很高了,而表格的一些高级操作,比如过滤,搜索等,如果你写了方法,那么确实要测试一下,这个实现思路跟utils/index.ts里面的方法类似,模拟的时候操作了一遍,就能覆盖到。

好了,上面这个例子希望大家去尝试,加深一下组件测试。

  • Vue 中文网
  • Vue github
  • Vue.js 是一套构建用户界面(UI)的渐进式JavaScript框架

库和框架的区别

  • 我们所说的前端框架与库的区别?

Library

库,本质上是一些函数的集合。每次调用函数,实现一个特定的功能,接着把控制权交给使用者

  • 代表:jQuery
  • jQuery这个库的核心:DOM操作,即:封装DOM操作,简化DOM操作

Framework

框架,是一套完整的解决方案,使用框架的时候,需要把你的代码放到框架合适的地方,框架会在合适的时机调用你的代码

  • 框架规定了自己的编程方式,是一套完整的解决方案
  • 使用框架的时候,由框架控制一切,我们只需要按照规则写代码

主要区别

  • You call Library, Framework calls you
  • 核心点:谁起到主导作用(控制反转)
    • 框架中控制整个流程的是框架
    • 使用库,由开发人员决定如何调用库中提供的方法(辅助)
  • 好莱坞原则:Don't call us, we'll call you.
  • 框架的侵入性很高(从头到尾)

MVVM的介绍

  • MVVM,一种更好的UI模式解决方案
  • 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM - 科普

MVC

  • M: Model 数据模型(专门用来操作数据,数据的CRUD)
  • V:View 视图(对于前端来说,就是页面)
  • C:Controller 控制器(是视图和数据模型沟通的桥梁,用于处理业务逻辑)

MVVM组成

  • MVVM => M / V / VM
  • M:model数据模型
  • V:view视图
  • VM:ViewModel 视图模型

优势对比

  • MVC模式,将应用程序划分为三大部分,实现了职责分离
  • 在前端中经常要通过 JS代码 来进行一些逻辑操作,最终还要把这些逻辑操作的结果现在页面中。也就是需要频繁的操作DOM
  • MVVM通过数据双向绑定让数据自动地双向同步
    • V(修改数据) -> M
    • M(修改数据) -> V
    • 数据是核心
  • Vue这种MVVM模式的框架,不推荐开发人员手动操作DOM

Vue中的MVVM

虽然没有完全遵循 MVVM 模型,Vue 的设计无疑受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的简称) 这个变量名表示 Vue 实例

学习Vue要转化思想

  • 不要在想着怎么操作DOM,而是想着如何操作数据!!!

起步 - Hello Vue

  • 安装:npm i -S vue
<!-- 指定vue管理内容区域,需要通过vue展示的内容都要放到找个元素中  通常我们也把它叫做边界 数据只在边界内部解析-->
<div id="app">{{ msg }}</div>

<!-- 引入 vue.js -->
<script src="vue.js"></script>

<!-- 使用 vue -->
<script>
  var vm = new Vue({
    // el:提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标
    el: '#app',
    // Vue 实例的数据对象,用于给 View 提供数据
    data: {
      msg: 'Hello Vue'
    }
  })
</script>

Vue实例

  • 注意 1:先在data中声明数据,再使用数据
  • 注意 2:可以通过 vm.$data 访问到data中的所有属性,或者 vm.msg
var vm = new Vue({
  data: {
    msg: '大家好,...'
  }
})

vm.$data.msg === vm.msg // true

数据绑定

  • 最常用的方式:Mustache(插值语法),也就是 {{}} 语法
  • 解释:{{}}从数据对象data中获取数据
  • 说明:数据对象的属性值发生了改变,插值处的内容都会更新
  • 说明:{{}}中只能出现JavaScript表达式 而不能解析js语句
  • 注意:Mustache 语法不能作用在 HTML 元素的属性上
<h1>Hello, {{ msg }}.</h1>
<p>{{ 1 + 2 }}</p>
<p>{{ isOk ? 'yes': 'no' }}</p>

<!-- !!!错误示范!!! -->
<h1 title="{{ err }}"></h1>

双向数据绑定 Vue two way data binding

  • 双向数据绑定:将DOM与Vue实例的data数据绑定到一起,彼此之间相互影响
    • 数据的改变会引起DOM的改变
    • DOM的改变也会引起数据的变化


  • 原理:Object.defineProperty中的getset方法
    • gettersetter:访问器
    • 作用:指定读取或设置对象属性值的时候,执行的操作


  • Vue - 深入响应式原理
  • MDN - Object.defineProperty()
/*  defineProperty语法 介绍 */
var obj = {}
Object.defineProperty(obj, 'msg', {
  // 设置 obj.msg = "1" 时set方法会被系统调用 参数分别是设置后和设置前的值
  set: function (newVal, oldVal) {  },
  // 读取 obj.msg 时get方法会被系统调用
  get: function ( newVal, oldVal ) {}
})

Vue双向绑定的极简实现

  • 剖析Vue原理&实现双向绑定MVVM
<!-- 示例 -->
<input type="text" id="txt" />
<span id="sp"></span>

<script>
var txt = document.getElementById('txt'),
    sp = document.getElementById('sp'),
    obj = {}

// 给对象obj添加msg属性,并设置setter访问器
Object.defineProperty(obj, 'msg', {
  // 设置 obj.msg  当obj.msg反生改变时set方法将会被调用  
  set: function (newVal) {
    // 当obj.msg被赋值时 同时设置给 input/span
    txt.value = newVal
    sp.innerText = newVal
  }
})

// 监听文本框的改变 当文本框输入内容时 改变obj.msg
txt.addEventListener('keyup', function (event) {
  obj.msg = event.target.value
})
</script>

动态添加数据的注意点

  • 注意:只有data中的数据才是响应式的,动态添加进来的数据默认为非响应式
  • 可以通过以下方式实现动态添加数据的响应式
    • 1 Vue.set(object, key, value) - 适用于添加单个属性
    • 2 Object.assign() - 适用于添加多个属性


var vm = new Vue({
  data: {
    stu: {
      name: 'jack',
      age: 19
    }
  }
})

/* Vue.set */
Vue.set(vm.stu, 'gender', 'male')

/* Object.assign 将参数中的所有对象属性和值 合并到第一个参数 并返回合并后的对象*/
vm.stu = Object.assign({}, vm.stu, { gender: 'female', height: 180 })

异步DOM更新

  • 说明:Vue 异步执行 DOM 更新,监视所有数据改变,一次性更新DOM
  • 优势:可以去除重复数据,对于避免不必要的计算和 避免重复 DOM 操作上,非常重要
  • 如果需要那到更新后dom中的数据 则需要通过 Vue.nextTick(callback):在DOM更新后,执行某个操作(属于DOM操作)
    • 实例调用vm.$nextTick(function () {})


methods: {
  fn() {
    this.msg = 'change'
    this.$nextTick(function () {
      console.log('$nextTick中打印:', this.$el.children[0].innerText);
    })
    console.log('直接打印:', this.$el.children[0].innerText);
  }
}

推荐大家使用Fundebug,一款很好用的BUG监控工具~


.em字体设置

body {font-size:100%;}
h1 {font-size:3.75em;}
h2 {font-size:2.5em;}
p {font-size:0.875em;}
复制代码

2.背景图标居右

.aa{
    background-image: url(arrow.png)no-repeat right center;
    background-image:url(nav-bar.jpg);
    background-repeat:no-repeat;
    background-position:right center;
}
复制代码

3.文本框超出部分显示省略号:

overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;    
复制代码


我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年我花了一个月整理了一份最适合2020年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。

一蒙版出现禁止页面滚动

1 window.onscroll=function(){
    document.body.scrollTop = 0
};

2 $('html,body').animate({scrollTop:'0'},100);
$(".tan").bind('touchmove',function(e){  //禁止弹出框出来时进行滑动 
    e.preventDefault();
});
3 document.body.style.overflow='hidden';
若键盘点击的话,就要加上:
var move=function(e){
    e.preventDefault && e.preventDefault();
    e.returnValue=false;
    e.stopPropagation && e.stopPropagation();
    return false;
}
var keyFunc=function(e){
    if(37<=e.keyCode && e.keyCode<=40){
        return move(e);
    }
}
document.body.onkeydown=keyFunc;

复制代码

二、按钮点击事件

var button=$(':button');
button.on('click',function(){
    button.css('background-color','white');
    $(this).css('background-color','#FB3336');
})
复制代码

三、安卓手机里,h5页面没有充满body导致左右滑动的问题

首先声明一下:

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
然后
html,body {width:100%;height:100%;overflow-x:hidden;}

复制代码

四、关于页面左右滚动的问题

1.<body scoll=no> 全禁止
2.<body style="overflow:scroll;overflow-y:hidden"> 禁止纵向滚动条
3.<body style="overflow:scroll;overflow-x:hidden"> 禁止纵向滚动条
4.overflow属性: 检索或设置当对象的内容超过其指定高度及宽度时如何显示内容
5.overflow: auto; 在需要时内容会自动添加滚动条
6.overflow: scroll; 总是显示滚动条
7.overflow-x: hidden; 禁止横向的滚动条
8.overflow-y: scroll; 总是显示纵向滚动条
复制代码

五、 content && header之间的亮条怎么消除

.mui-bar-nav{
    -webkit-box-shadow: none;
    box-shadow: none;
}
复制代码

六、删除选项

$(".shanchu").click(function(){
    $(this).parent().remove()
})
复制代码

七、表单提交按钮时,鼠标放在上面显示小手的方法:

需要对元素属性的css的cursor进行设置
1、default    默认光标(通常是一个箭头)
2、auto   默认。浏览器设置的光标。 
3、crosshair   光标呈现为十字线。    
4、pointer    光标呈现为指示链接的指针(一只手)    
5、move    此光标指示某对象可被移动。    
6、e-resize    此光标指示矩形框的边缘可被向右(东)移动。    
7、ne-resize    此光标指示矩形框的边缘可被向上及向右移动(北/东)。    
8、nw-resize    此光标指示矩形框的边缘可被向上及向左移动(北/西)。    
9、n-resize    此光标指示矩形框的边缘可被向上(北)移动。    
10、se-resize    此光标指示矩形框的边缘可被向下及向右移动(南/东)。    
11、sw-resize    此光标指示矩形框的边缘可被向下及向左移动(南/西)。  
12、s-resize    此光标指示矩形框的边缘可被向下移动(南)。    
13、w-resize    此光标指示矩形框的边缘可被向左移动(西)。    
14、text    此光标指示文本。    
15、wait    此光标指示程序正忙(通常是一只表或沙漏)。    
16、help    此光标指示可用的帮助(通常是一个问号或一个气球)。    
要实现鼠标移上去显示手形、需要在你的提交按钮上增加css cursor属性,并将它的值设置为pointer;
如下:<input type="submit" name="submit" value="发布留言" class="subimt" onclick="display_alert()" style="cursor:pointer" />
复制代码

八、怎么清除table里面tbody的内容

$("#test tbody").html("");
复制代码

九、动态获取表格的行数

var bv=$("#tabd tr").length-1;
$("#sp4").html(bv);     //动态的获取注数
复制代码

十、多个按钮点击变色,再点击还原

$(".jixuan  input[type=button]").toggle(function(){
    $(this).css("background-color","yellow");
    $(this).css("cursor","pointer")
    },function(){
    $(this).css("background-color","white");
    $(this).css("cursor","pointer");
}) 
复制代码

十一、单选按钮顾名思义用于单选的场合,例如,性别,职业的选择等,语法如下:

<input type="radio" name="gender" value="男" checked />
常用属性迅美科技整理如下:
1.type="radio"
type属性设置为radio,表示产生单一选择的按钮,让用户单击选择;
2.name="gender"
radio组件的名称,name属性值相同的radio组件会视为同一组radio组件,而同一组内只能有一个radio组件被选择;
3.value="男"
radio组件的值,当表单被提交时,已选择的radio组件的value值,就会被发送进行下一步处理, radio组件的value属性设置的值 
无法从外观上看出,所以必须在radio组件旁边添加文字,此处的文字只是让用户了解此组件的意思.
4.checked
设置radio组件为已选择,同一组radio组件的name性情值必须要相同
复制代码

十二、网页中,公共头部和侧边栏的引用

1、<?php include("header.html");?>
2、使用ssi技术页面生成shtml文件,只用在头部文件位置加入<!--#include file="header.htm" -->,
然后修改的时候只要修改header.htm文件就可以了。使用shtml的好处是对搜索引擎比较友好,需要处理的文件在服务器端完成的,
不会加重访问者的浏览器负担。
复制代码

十三、锚点链接上下定位偏移解决

1、JS解决的方法

if (window.location.hash.indexOf('#') >= 0) {
    $('html,body').animate({
    scrollTop: ($(window.location.hash).offset().top - 50) + "px"
    },
    300);
}; //主要修复评论定位不准确BUG
$('#comments a[href^=#][href!=#]').click(function() {
    var target = document.getElementById(this.hash.slice(1));
    if (!target) return;
    var targetOffset = $(target).offset().top - 50;
    $('html,body').animate({
        scrollTop: targetOffset
    },
    300);
    return false;
}); //主要修复评论定位不准确BUG
复制代码

2、解决办法

能用css自然不想用js解决,因为在加载方面,css总是先加载,并且速度很快。

typecho的评论HTML结构是这样的:

<li id="comment-277" class="comment-body comment-child comment-level-odd comment-even comment-by-author">
我们给comment-body加上css

.comment-body {
    position: relative;
    padding-top: 50px;
    margin-top: -50px;
}
/*修复评论跳转定位问题*/
完美兼容chrome和Firefox,其他浏览器未测试。
复制代码

十四、蒙版弹出禁止蒙版后面的内容滚动

.ovfHiden{overflow: hidden;height: 100%;}
$('.bzh .l1 a').click(function(){
    $(".baok").show();
    $(".baod").show();
    $('html,body').addClass('ovfHiden');
});
$('.baod .img1').click(function(){
$('html,body').removeClass('ovfHiden');
    $(".baok").hide();
    $(".baod").hide();
});
复制代码

十五、获取复选框点击的次数

$("#compute").click(function(){
    $('input').live('click',function(){ 
    //alert($('input:checked').length); 
    $("#show").html($('input:checked').length);
    });
});
复制代码

十六、Tab选项卡切换

1.js

$('footer ul li').click(function(){
    var index = $(this).index();
    $(this).attr('class',"content").siblings('ul li').attr('class','ss');
    $('.content').eq(index).show(200).siblings('.content').hide();
    });

$('.ka ul li').click(function(){
    var index = $(this).index();
    $(this).attr('class',"zi").siblings('ul li').attr('class','ll');
    $(this).parent().next().find(".zi").hide(). eq(index).show();
});
复制代码

2.html

<div class="carindex-cnt">
        <ul class="nav">
            <li>续保方案</li>
            <li>热销方案</li>
            <li>自定义方案</li>
        </ul>
        <div class="tabcontent">
            <div class="zi">
                <p class="altp">此方案为您上一年的投保记录</p>
                <ul class="xiur">
                    <li>
                        <label for="saveType2">交强险</label>
                        <div  class="right-cnt">
                            <input type="text" class="coverage" disabled="disabled" value="不投保"/>
                            <ul class="datas" style="display: none;">
                                <li ref="1">投保</li>
                                <li ref="2">不投保</li>
                            </ul>
                        </div>
                    </li>
                    <li>
                        <label for="saveType2">商业险</label>
                        <div  class="right-cnt">
                            <input type="text" class="coverage" disabled="disabled" value="不投保"/>
                            <ul class="datas" style="display: none;">
                                <li ref="1">投保</li>
                                <li ref="2">不投保</li>
                            </ul>
                        </div>
                    </li>
                </ul>
                <p class="title">商业主险</p>
                <ul class="xiur">
                    <li>
                        <span>车辆损失险</span>
                        <label for="abatement0" class="labels">
                            <input class="mui-checkbox checkbox-green" type="checkbox" name="abatement" >
                        </label>
                        <div  class="right-cnt">
                            <input type="text" class="coverage" disabled="disabled" value="不投保"/>
                            <ul class="datas" style="display: none;">
                                <li ref="1">投保</li>
                                <li ref="2">不投保</li>
                            </ul>
                        </div>
                    </li>
                  
                </ul>
            </div>
            <div class="zi" style="display: none">
                <ul class="xiur">
                    <li>
                        <label for="saveType2">交强险</label>
                        <div  class="right-cnt">
                            <input type="text" class="coverage" disabled="disabled" value="不投保"/>
                            <ul class="datas" style="display: none;">
                                <li ref="1">投保</li>
                                <li ref="2">不投保</li>
                            </ul>
                        </div>
                    </li>
                    <li>
                        <label for="saveType2">商业险</label>
                        <div  class="right-cnt">
                            <input type="text" class="coverage" disabled="disabled" value="不投保"/>
                            <ul class="datas" style="display: none;">
                                <li ref="1">投保</li>
                                <li ref="2">不投保</li>
                            </ul>
                        </div>
                    </li>
                </ul>
            </div>
        </div>
</div>  
复制代码

3.js

$('.nav li').click(function () {
    var index = $(this).index();
    $(this).parent().next().find(".zi").hide().eq(index).show();
})

复制代码

十七、form表为空时,提交按钮禁用

$(function(){            
    $('.main button').click(function(){
        if(($('.ip1').val() !="") && ($('.ip2').val() !="")){
            $('.main button').css('background','#ff8100');
            $('.main button').attr('disabled', true);
            }else{
            $('.main button').css('background','#D0D0D0');
            $('.main button').attr('disabled', false);
        }
    })
})
复制代码

十八、上拉事件和下拉事件

$(window).scroll(function(){
    var scrollTop = $(this).scrollTop();               //滚动条距离顶部的高度
    var scrollHeight =$(document).height();                   //当前页面的总高度
    var windowHeight = $(this).height();                   //当前可视的页面高度
    if(scrollTop + windowHeight >= scrollHeight){    //距离顶部+当前高度 >=文档总高度 即代表滑动到底部
        alert("上拉加载,要在这调用啥方法?");
    }else if(scrollTop<=0){         //滚动条距离顶部的高度小于等于0
        alert("下拉刷新,要在这调用啥方法?");
    }
});                                                          ——>移动端

$(function(){    
    $(window).scroll(function() {  
        var scrollTop = $(this).scrollTop(),scrollHeight = $(document).height(),windowHeight = $(this).height();  
        var positionValue = (scrollTop + windowHeight) - scrollHeight;  
        if (positionValue == 0) {  
            //do something  
        }  
    });  
});  
复制代码

十九、左滑和右滑事件

var obj;
var startx;
var starty;
var overx;
var overy;
    for(var i=1;i<=$("li").length;i++){          //为每个li标签添加事件
    obj = document.getElementById(i);       //获取this元素
    evenlistener(obj);      //调用evenlistener函数并将dom元素传入,为该元素绑定事件
}

function evenlistener(obj){
    obj.addEventListener('touchstart', function(event) {        //touchstart事件,当鼠标点击屏幕时触发
    startx = event.touches[0].clientX;              //获取当前点击位置x坐标
    starty = event.touches[0].clientY;              //获取当前点击位置y坐标
    $(".sdf").text("x:"+startx+",y:"+starty+"")     //赋值到页面显示
    } , false);         //false参数,设置事件处理机制的优先顺序,具体不多说,true优先false
    obj.addEventListener('touchmove', function(event) {         //touchmove事件,当鼠标在屏幕移动时触发
    overx = event.touches[0].clientX;           //获取当前点击位置x坐标
    overy = event.touches[0].clientY;           //获取当前点击位置y坐标
    var $this = $(this);            //将dom对象转化为jq对象,由于项目用到jquery,直接使用其animate方法

    if(startx-overx>10){         //左滑动判断,当左滑动的距离大于开始的距离10进入
    $($this).animate({marginLeft:"-55px"},150);         //实现左滑动效果
    }else if(overx-startx>10){       //右滑动判断,当右滑动的距离大于开始的距离10进入
    $($this).animate({marginLeft:"0px"},150);           //恢复
    }
} , false);
    obj.addEventListener('touchend', function(event) {          //touchend事件,当鼠标离开屏幕时触发,项目中无用到,举例
    $(".sf").text("x:"+overx+",y:"+overy+"")
    } , false);
}
复制代码

二十、各大浏览器的判断

var Sys = {};
var ua = navigator.userAgent.toLowerCase();
var s;
(s = ua.match(/rv:([\d.]+)\) like gecko/)) ? Sys.ie = s[1] :
(s = ua.match(/msie ([\d.]+)/)) ? Sys.ie = s[1] :
(s = ua.match(/firefox\/([\d.]+)/)) ? Sys.firefox = s[1] :
(s = ua.match(/chrome\/([\d.]+)/)) ? Sys.chrome = s[1] :
(s = ua.match(/opera.([\d.]+)/)) ? Sys.opera = s[1] :
(s = ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = s[1] : 0;
if (Sys.ie){
    $("*").css({fontFamily:"微软雅黑"});
};
if (window.ActiveXObject){
Sys.ie = ua.match(/msie ([\d.]+)/)[1];
if (Sys.ie<=9){
    alert('你目前的IE版本为'+Sys.ie+'版本太低,请升级!');
    location.href="http://windows.microsoft.com/zh-CN/internet-explorer/downloads/ie";
    }
}
var UA=navigator.userAgent;
if(is360se = UA.toLowerCase().indexOf('360se')>-1 ){

}else{
    $("*").css({fontFamily:"微软雅黑"});
}

360浏览器基于IE内核的,360急速浏览器内核基于谷歌的
复制代码

二十一、form表单中点击button按钮刷新问题

button,input type=button按钮在IE和w3c,firefox浏览器区别: 
1、当在IE浏览器下面时,button标签按钮,input标签type属性为button的按钮是一样的功能,不会对表单进行任何操作。 
2、但是在W3C浏览器,如Firefox下就需要注意了,button标签按钮会提交表单,而input标签type属性为button不会对表单进行任何操作。
为button按钮增加一个type=”button”属性。
复制代码

二十二、textrare文字输入提示:

<textarea name="" id="sign" maxlength=30 onKeyUp="keypress1()"></textarea>
<div class="tish">
<span id="number">0</span><span>/30</span>
</div>

function keypress1() //text输入长度处理 
{ 
    var text1=document.getElementById("sign").value; 
    var len=text1.length; 
    var show=len; 
    document.getElementById("number").innerText=show; 
} 
复制代码

二十三、iframe操作

1:父页面操作iframe子页面

$('#ifrme').load(function(){
$('#ifrme').contents().find('.baod .img1').click(function(){
    $(.ifrme').contents().find('.baod').hide();
    $('.baok',window.parent.document).hide();
    $('html,body',window.parent.document).removeClass('ovfHiden');
    });
})
* .ifrme父页面的ID为iframe的父级
    .baod .img1是iframe页面里的元素
复制代码

2:子页面操作父页面

$('.baod .bt1').click(function(){
    $('.baod').hide();
    $('.edit',window.parent.document).hide();
    $(".baok", window.parent.document).hide(); 
    $('html,body',window.parent.document).removeClass('ovfHiden');
});
*.baod .bt1子页面里的元素
window.parent.document父级窗口
.edit父级页面元素
复制代码

二十四、toggle开关切换图标或是元素的隐藏

$('.other .pg').click(function(){
    $(this).toggleClass ("pots");
    $('.below').slideToggle(300);
})

* .other .pg元素名称
pots 点击元素要切换的图标(以background()形式的图标)
.below要进行toggle的内容
复制代码

二十五、弹框居中

$(".btnDel").click(function() {  
//$(".box-mask").css({"display":"block"});  
    $(".box-mask").fadeIn(500);  
    center($(".box"));  
    //载入弹出窗口上的按钮事件  
    checkEvent($(this).parent(),            $(".btnSure"), $(".btnCancel"));  
});  *center  弹框名称

function center(obj) {  
    //obj这个参数是弹出框的整个对象  
    var screenWidth = $(window).width(), screenHeigth = $(window).height();  
    //获取屏幕宽高  
    var scollTop = $(document).scrollTop();  
    //当前窗口距离页面顶部的距离  
    var objLeft = (screenWidth - obj.width()) / 2;  
    ///弹出框距离左侧距离  
    var objTop = (screenHeigth - obj.height()) / 2 + scollTop;  
    ///弹出框距离顶部的距离  
    obj.css({  
        left:objLeft + "px",  
        top:objTop + "px"  
    });  
    obj.fadeIn(500);  
    //弹出框淡入  
    isOpen = 1;  
    //弹出框打开后这个变量置1 说明弹出框是打开装填  
    //当窗口大小发生改变时  
    $(window).resize(function() {  
        //只有isOpen状态下才执行  
        if (isOpen == 1) {  
            //重新获取数据  
            screenWidth = $(window).width();  
            screenHeigth = $(window).height();  
            var scollTop = $(document).scrollTop();  
            objLeft = (screenWidth - obj.width()) / 2;  
            var objTop = (screenHeigth - obj.height()) / 2 + scollTop;  
            obj.css({  
                left:objLeft + "px",  
                top:objTop + "px"  
            });  
            obj.fadeIn(500);  
        }  
});  
    //当滚动条发生改变的时候  
$(window).scroll(function() {  
    if (isOpen == 1) {  
        //重新获取数据  
        screenWidth = $(window).width();  
        screenHeigth = $(window).height();  
        var scollTop = $(document).scrollTop();  
        objLeft = (screenWidth - obj.width()) / 2;  
        var objTop = (screenHeigth - obj.height()) / 2 + scollTop;  
        obj.css({  
            left:objLeft + "px",  
            top:objTop + "px"  
        });  
        obj.fadeIn(500);  
    }  
});  
复制代码

二十六、css和js进行奇偶选择器

css

:nth-child(odd){background-color:#FFE4C4;}奇数行
:nth-child(even){background-color:#F0F0F0;}偶数行
复制代码

js

$("table  tr:nth-child(even)").css("background-color","#FFE4C4");    //设置偶数行的背景色
$("table  tr:nth-child(odd)").css("background-color","#F0F0F0");    //设置奇数行的背景色
复制代码

二十七、jQuery中live()使用报错,TypeError: $(...).live is not a function

jquery中的live()方法在jquery1.9及以上的版本中已被废弃了,如果使用,会抛出TypeError: $(...).live is not a function错误。

解决方法:

之前的用法:

.live(events, function)  

新方法:

.on(eventType, selector, function)

若selector不需要,可传入null


例子1:

之前:

$('#mainmenu a').live('click', function)

之后:

$('#mainmenu').on('click', 'a', function)


例子2:

之前:

$('.myButton').live('click', function)

之后(应使用距离myButton最近的节点):

$('#parentElement').on('click', ‘.myButton’, function)

若不知最近的节点,可使用如下的方法:

$('body').on('click', ‘.myButton’, function)
复制代码

二十八、iframe滚动条问题

iframe嵌入的滚动条可以用iframe里面页面的大小覆盖掉iframe的滚动条
复制代码

二十九、点击图片下载(不用新窗口打开)

<a class="downs"  style="display:'+display+'" onclick="downimg(\''+list[i].skuTieTu+'\')">下载</a>
复制代码

js方法

/**
* 图片单独下载
*/
function downimg(skuTieTu){
    console.log(skuTieTu)
    let src = skuTieTu;
    var canvas = document.createElement('canvas');
    var img = document.createElement('img');
    img.onload = function(e) {
    canvas.width = img.width;
    canvas.height = img.height;
    var context = canvas.getContext('2d');
    context.drawImage(img, 0, 0, img.width, img.height);
    canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
    canvas.toBlob((blob)=>{
        let link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = 'zzsp'; 
        link.click();  
    }, "image/jpeg");
}
img.setAttribute("crossOrigin",'Anonymous');
img.src = src;
复制代码

三十、ajax里面条件判断

$.ajax({
    type: "post",
    data: 
    contentType:
    url: 
    beforeSend: function () {
        if(){
        
        }else{
        
        };
    },
    success: function (data) {
        alert("保存失败");
    },
    error: function (data) {
        alert("保存成功");
    }
});
复制代码

三十一、ajax里面在数据请求之前加layui.load()时,请求状态必须是异步的才行( async: true)

$.ajax({
    type:"post",
    url: API,
    data: {
        'a':'project.kujiale.plan.YongliaoUser'
    },
    dataType: "json",
    async: true,
    beforeSend: function () {
        layer.load(1);
    },
    success: function(data) {   
        var item =data.data;
        list = item
        if(data.code==0){
            layer.closeAll();
            var url = '/module/designplan/searchplan/searchlist.jsp';
            layer.open({
            type: 2,
            title: "搜索方案",
            shadeClose: true,
            shade: 0.8,
            area: ['700px','500px'],
            content: [url]
            });
        }else{
            layer.msg(data.msg);
        }
    }
});

复制代码

三十二、js根据元素的属性获取到改元素其他属性的值

jquery
$("a[id=search]").attr("data-search")

原生js
document.querySelector("a[id=search]").getAttribute("data-search") //根据当前元素的属性获取该元素其他属性的值

document.querySelector("a[id=search]").text //根据当前属性获取该元素的值
document.querySelector("a[id=search]").innerText //根据当前属性获取该元素的值    
复制代码

三十三、数组对象提交时转化问题

JSON.stringify(userList)
复制代码

三十四、layui使用

1、关闭弹窗

layer.msg('分配成功',{time: 1000},function () {
    var index = parent.layer.getFrameIndex(window.name);
    parent.layer.close(index);
})
复制代码

2、关闭弹窗,刷新页面

window.location.reload();//刷新当前页面
window.parent.location.reload();//刷新父级页面
复制代码

三十五、js创建下载方式

download(data.data);

function downpdf(data){
    var link = document.createElement('a');
    link.href = data;
    link.target = '_blank';
    link.click();
    delete link;
}
复制代码

三十六、高阶函数

const isYoung = age => age < 25;

const message = msg => "He is "+ msg;

function isPersonOld(age, isYoung, message) {
    const returnMessage = isYoung(age)?message("young"):message("old");
    return returnMessage;
}

// passing functions as an arguments
    console.log(isPersonOld(13,isYoung,message))
// He is young
复制代码

递归

递归是一种函数在满足一定条件之前调用自身的技术。只要可能,最好使用递归而不是循环。你必须注意这一点,浏览器不能处理太多递归和抛出错误。
下面是一个演示递归的例子,在这个递归中,打印一个类似于楼梯的名称。我们也可以使用for循环,但只要可能,我们更喜欢递归。
复制代码
function printMyName(name, count) {
    if(count <= name.length) {
        console.log(name.substring(0,count));
        printMyName(name, ++count);
    }
}

console.log(printMyName("Bhargav", 1));

/*
    B
    Bh
    Bha
    Bhar
    Bharg
    Bharga
    Bhargav
*/

// withotu recursion
var name = "Bhargav"
var output = "";
for(let i=0; i<name.length; i++) {
    output = output + name[i];
    console.log(output);
}


作者:山水有轻音
链接:https://juejin.im/post/6873003814065012750