前言

我们在上学期做网页开发的时候,只是在留言板那个部分简单讲了一下表单,我们本章节讲如何使用js处理我们的表单,我们顺带一起把表单基础内容一起讲解,方便大家理解。在早期,没有js的时候,我们的提交数据都是由服务器端处理的,对表单数据的过滤、验证合法性等等,这样会导致服务器压力很大,特别是用户量大的时候,这个时候就出现了我们的js验证表单,从而分担服务器处理表单的压力。

# Ⅰ、表单基础知识

在 HTML 中,表单是由<form>元素来表示的,在js中,表单对应的则是HTMLFormElement 类型。

# 1、获取表单form对象的方法

   <form id="myForm" name="yourForm"></form>
   // window.addEventListener('load',function(){},false);
   //表单基础

   //1、获取form对象的方法
   //id获取
   //let fm = document.getElementById('myForm');
   //console.log(fm);

   // form元素获取
   // let fm = document.getElementsByTagName('form')[0];
   // console.log(fm);

   // querySelector系列
   // let fm = document.querySelector('form');
   // fm = document.querySelectorAll('form')[0];
   // fm = document.querySelector('#myForm');
   // console.log(fm);

   //HTML DOM方式获取,我们在章节15讲过用HTML DOM方式操作表格
   console.log(document);
   let fm = document.forms[0];//HTML DOM方式通过0下标获取
   fm = document.forms['myForm'];//HTML DOM方式通过id下标获取
   fm = document.forms['yourForm'];//HTML DOM方式通过name下标获取
   console.log(fm);

# 2、提交表单

# ① 默认提交form表单中,按钮type="submit"

<form id="myForm" name="yourForm" class="my-5 flex justify-center">
    账号:<input type="text" name="user" value="abc" />
    <input type="button" value="输入框类型换button提交">
    <input type="submit" value="输入框类型换submit提交">
    <button>提交</button>
</form>
//我们点击提交按钮,发现浏览器地址栏多了 ?user=abc  说明在本页提交的
//而且带?,意思是默认以get方式提交的,关于提交方式get/post,我们后面会讲
//当然如果你把按钮类型,由 type="submit" 改成 type="button" 就不能提交了

# ② js中表单提交,及阻止提交

//传统提交(脚本模型)
let fm = document.getElementById('myForm');
// fm.onsubmit = function(){}  

//现代事件绑定提交
// fm.addEventListener('submit',function(){},false);

//阻止提交(阻止默认行为)
fm.addEventListener('submit',function(e){
   e.preventDefault();
},false);

//思考:
//1. submit事件,为什么要用form对象来触发呢?为什么不是input中的submit按钮触发呢?
<input id="sub" type="submit" value="提交">
let sub = document.getElementById('sub');
sub.addEventListener('submit',function(e){
    e.preventDefault();//发现阻止不了,可以提交
},false);
//注意了:
//1. 把submit提交事件注册到input中的submit按钮,是无法触发submit提交事件的;
//2. 必须把submit事件绑定到form表单对象上,才可以触发submit事件
//3. 因为form元素是看不见的东西,所以触发submit提交事件的流程是:点击input中的submit按钮而已

# ③ 让没有提交功能的按钮(其它元素),完成表单提交,使用的是form.submit()方法

 let sub = document.getElementById('sub');
 sub.addEventListener('submit',function(e){
     e.preventDefault();//发现阻止不了,可以提交
     alert(123);//弹窗也无法执行
 },false);

//虽然说提交按钮无法执行submit提交事件,需要在form上执行,但是按钮有点击事件
let sub = document.getElementById('sub');
let fm = document.getElementById('myForm');
sub.addEventListener('click',function(e){
    //alert(123);
    fm.submit();//让form执行submit方法提交数据
},false);

//也可以是其他元素
<strong id="strongsub">我是提交文字</strong>
let strongsub = document.getElementById('strongsub');
let fm = document.getElementById('myForm');
strongsub.addEventListener('click',function(){
    fm.submit();
},false);

strongsub.onclick = function(){
    fm.submit();
}

# ④ 实现使用键盘 CTRL + enter(回车键)提交数据

//判断是否按下了ctrl键,前面事件对象已经讲了e.ctrlKey为true就是按下了
//判断是否按下了回车键,事件对象中的键码 e.keyCode , 回车键的键码是13
//对页面数据进行提交,以document作为事件绑定对象
document.addEventListener('keydown',function(e){
    //console.log('是否按下ctrl键', e.ctrlKey);//是否按下ctrl键
    //console.log('键码', e.keyCode);//回车键键码 13

    if(e.ctrlKey && e.keyCode == 13){
        console.log('用户按下了ctrl键和回车键');
        let fm = document.getElementById('myForm');
        fm.submit();
    }
},false);

注意:在表单中尽量避免使用 name="submit"或 id="submit"等命名,这会和 submit()方法发生冲突导致无法提交。

# ⑤ 预防表单数据重复提交

提交数据最大的问题就是重复提交表单。因为各种原因(比如你当时的网络非常差),当一条数据提交到服务器的时候会出现延迟等长时间没反映,导致用户不停的点击提交,从而使得重复提交了很多相同的请求,会造成错误、或写入数据库多条相同信息。我们可以来模拟一下:

let fm = document.getElementById('myForm');
fm.addEventListener('submit',function(e){
   e.preventDefault();
   console.log('提交数据');

   //模拟延迟
   setTimeout(function(){
      fm.submit();
   }, 3000);

},false);

//为了防止用户不停的提交数据,有两种解决方案:
//第一种就是提交之后,立刻禁用点击按钮,让点击按钮不在具有提交功能
let fm = document.getElementById('myForm');
fm.addEventListener('submit',function(e){
   e.preventDefault();
   console.log('提交数据');
   //第一次提交后讲提交按钮禁用
   // document.getElementById('sub').disabled = 'disabled';
   //document.getElementById('sub').setAttribute('disabled','disabled');

   //模拟延迟
   setTimeout(function(){
      fm.submit();
      document.getElementById('sub').removeAttribute('disabled');//恢复按钮可用
   }, 3000);

},false);
//但第一种方法,只限于通过按钮进行提交的,在其他元素上或者键盘组合方式,又不行了

//第二种方法:提交之后取消后续的表单提交操作(状态标记法)
let fm = document.getElementById('myForm');
let flag = false;
fm.addEventListener('submit',function(e){
   e.preventDefault();

   if(flag == true) return;
   flag = true;//表示我已经提交过一次了

   console.log('提交数据');
   
   //模拟延迟
    setTimeout(function(){
        fm.submit();
        flag = false;
    }, 3000);

},false);

# ⑥ 表单重置:按钮重置type="reset",js重置 form.reset()方法

//按钮重置
<input type="reset" value="重置" />

//js重置
<strong id="strong_reset">重置</strong>
let strong_reset = document.getElementById('strong_reset');
strong_reset.onclick = function(){
    let fm = document.getElementById('myForm');
    fm.reset();
}

# ⑦ 使用HTML DOM获取表单控件元素

# 1. elements属性获取表单控件元素集合

let fm = document.getElementById('myForm');
console.log(fm);
console.log(fm.elements);//HTMLFormControlsCollection 表单控件元素集合
//表单控件元素:input, textarea,select,checkbox,submit等
//获取表单控件数量
console.log(fm.elements.length);//非表单控件元素不会计算在内

console.log(fm.elements[0]);
console.log(fm.elements['username']);
console.log(fm.elements[0].name);


console.log(fm.elements[1]);
console.log(fm.elements['sub']);

# 2.多个表单字段都使用同一个 name,如单选框多选框

<input type="radio" name="sex" value="男" checked="checked"><input type="radio" name="sex" value="女" >//集合列表
let fm = document.getElementById('myForm');
console.log(fm.elements);
console.log(fm.elements['sex']);
console.log(fm.elements['sex'][0]);
console.log(fm.elements['sex'][0].checked);
console.log(fm.elements['sex'][1].checked);

# 3.常用属性

let fm = document.getElementById('myForm');
console.log(fm.elements[0]);
console.log(fm.elements[0].value); //获取和设置 value
console.log(fm.elements[0].form == fm); //查看当前字段所属表单
console.log(fm.elements[0].disabled = true); //禁用当前字段
console.log(fm.elements[0].type);//text
// fm.elements[0].type = 'checkbox'; //修改字段类型,极不推荐

# 4.表单共有字段方法:foucs():将光标焦点放在控件上,blur():讲焦点移开控件

let fm = document.getElementById('myForm');
fm.elements[0].focus();
// fm.elements[0].blur();

# 5.表单共有的字段事件:blur(当字段失去焦点时触发),change(对于<input>和<textarea>元素,在改变 value 并失去焦点时触发;对于<select>元素,在改变选项时触发),focus(当前字段获取焦点时触发)

let fm = document.getElementById('myForm');
let inputField = fm.elements[0];
//传统事件:onfocus
inputField.onfocus = function(){
    console.log('光标在账户输入框触发');
}
//现代事件绑定
inputField.addEventListener('focus',function(){
    console.log('光标在账户输入框触发');
},false);
inputField.addEventListener('blur',function(){
    console.log('光标离开账户输入框触发');
},false);
inputField.addEventListener('change',function(){
    console.log('输入内容改变,且失去光标触发');
},false);
inputField.addEventListener('input',function(){
    console.log('输入内容改变触发',this.value);
},false);

# Ⅱ、表单中的文本框

在 HTML 中,有两种方式来表现文本框:一种是单行文本框<input type="text">,一种是多行文本框<textarea>。虽然<input>在字面上有 value 值,而<textarea>却没有,但都可以通过 value 获取他们的值。
<textarea>多行的文本输入控件介绍

# ① input.value 和 textarea.value 获取输入的内容,defaultValue获取默认设置的value值

账号:<input type="text" name="username" value="abc">
<textarea name="content"  cols="30" rows="10">我是多行文本</textarea>

let fm = document.getElementById('myForm');
let inputField = fm.elements['username'];
let textareaField = fm.elements['content'];
console.log(inputField.value);
console.log(textareaField.value);
//说明:
//1.在html中,input有value属性,textarea没有value属性
//2.在js中,input和textarea都有value属性

//还有一个属性对应的是 defaultValue,可以得到原本的 value 值,不会因为值的改变而变化
let fm = document.getElementById('myForm');
let inputField = fm.elements['username'];
inputField.oninput = function(){
    console.log('value',this.value);
    console.log('defaultValue',this.defaultValue);
}

# ② 选择文本:使用 select()方法,可以将文本框里的文本选中,并且将焦点设置到文本框中

let fm = document.getElementById('myForm');
let inputField = fm.elements['username'];
inputField.select();

# ③ 选择部分文本:setSelectionRange()方法,这个方法接受两个参数:索引和长度。

let fm = document.getElementById('myForm');
let inputField = fm.elements['username'];
inputField.setSelectionRange(0,2); //从索引0位置开始,选到索引第二个位置,按键盘可以直接修改选定内容
// inputField.setSelectionRange(1,2); //从索引1位置开始,选到索引第二个位置
// inputField.setSelectionRange(0,inputField.value.length);//全部选定
inputField.focus(); //焦点移入

# ④ select 事件,选中文本框文本后触发

let fm = document.getElementById('myForm');
let inputField = fm.elements['username'];
//传统(脚本模型)
inputField.onselect = function(){
    console.log('你选择了内容')
}
//现代事件绑定
inputField.addEventListener('select',function(){
    console.log('你选择了内容')
},false);

# ⑤ 获取选择的文本,两个属性:selectionStart 和 selectionEnd

let fm = document.getElementById('myForm');
let inputField = fm.elements['username'];
//现代事件绑定
inputField.addEventListener('select',function(){
    console.log('你选择了内容')
    console.log('全部文本内容',this.value)
    console.log('selectionStart',this.selectionStart);
    console.log('selectionEnd',this.selectionEnd);

    //获取选择的文本字符串的substring方法
    console.log('选择的文本',this.value.substring(this.selectionStart,this.selectionEnd));
},false);

# ⑥ 文本过滤输入(表单验证,文本验证,输入框验证)

实际开发中,用户在输入文本的时候,必须按照指定字符输入,比如用户注册的时候,输入手机号,手机号一般11位,且是数字,你不能让用户输入中文英文,这就需要进行文本过滤。

//过滤输入模式:纯数字(屏蔽非数字键的输入)
//1.禁止或屏蔽非数字键的输入(阻止非数字键的默认行为即可)
//2.跟1相反,验证后取消,可以先输入非法字符,然后判断后,取消你刚输入的文本
//说明:阻止键盘输入在实际开发中还是用得比较少的,实际开发中更多用正则表达式判断输入合法性
//我们这个知识点只是给大家介绍一下,有这么个策略

let fm = document.getElementById('myForm');
let inputField = fm.elements['username'];
inputField.addEventListener('keypress',function(e){
    console.log(e.charCode);
    //fromCharCode(ascii) - 静态方法,输出 Ascii 码对应值
    console.log(String.fromCharCode(e.charCode));
    //用正则表达式来获取文本是否为数字,(e.charCode > 0)为辅助,只限于放开光标键/退格键/删除键
    if(!(/\d/.test(String.fromCharCode(e.charCode))) && (e.charCode > 0)){
        e.preventDefault();//屏蔽掉不是数字键的键盘输入
    }

    //这个方案还是有很多漏洞:用户通过裁剪、复制和粘贴,中文输入依然可以输入非数字字符

},false);

# ⑦ 剪切事件:cut,复制事件:copy,粘贴事件:paste,禁用输入法

inputField.addEventListener('copy',function(e){
    //e.preventDefault();
    console.log('你按了复制,如 CTRL+C');
},false);
inputField.addEventListener('cut',function(e){
    //e.preventDefault();
    console.log('你按了剪切,如 CTRL+X');
},false);
inputField.addEventListener('paste',function(e){
    //e.preventDefault();
    console.log('你按了粘贴,如 CTRL+V');
},false);

//屏蔽中文输入法,但谷歌无效(屏蔽输入法这种方式用户体验不好,用户以为网站中毒了)
//inputField.style.imeMode = 'disabled';
//由于谷歌浏览器无法屏蔽中文输入法,所以选择第二种方式:正则表达式,将用户输入的非数字字符全部替换成空

# ⑧ 利用正则表达式将用户输入的非数字字符替换成空,只留下数字

let fm = document.getElementById('myForm');
let inputField = fm.elements['username'];
//验证数据非法后取消输入键盘字符
inputField.addEventListener('keyup',function(e){
    this.value = this.value.replace(/[^\d]/g,'');
},false);

//关于正则表达式,后面会开专题给大家讲解

# ⑨ 输入框自动切换焦点到下一个输入框(类似Tab键),maxlength属性可输入的最大字符长度

<input type="text" name="user1" maxlength="1" /> //只能写 1 个字符
<input type="text" name="user2" maxlength="2" /> //只能写 2 个字符
<input type="text" name="user3" maxlength="3" /> //只能写 3 个字符

let fm = document.getElementById('myForm');
fm.elements['user1'].addEventListener('keyup',tabnext,false);
fm.elements['user2'].addEventListener('keyup',tabnext,false);
fm.elements['user3'].addEventListener('keyup',tabnext,false);

function tabnext(e){
   //判断当前设置的长度maxlength 是否和我输入的长度一致,是一致的话
   if(this.value.length == this.maxLength){
      //循环遍历所有控件
      for(let i=0;i<fm.elements.length;i++){
          //在循环遍历过程中,如果发现循环的这个正是你现在输入的这个文本框,输入满了,则把光标移到下一个输入框
          if(fm.elements[i] == this){
            fm.elements[i + 1].focus();
            return;
          }
      }
   }
}

# Ⅲ、表单中的下拉列表(选择框)

下拉列表是通过<select>和<option>元素创建的,更多了解选择框
关于下拉列表,我们在第一学期开发网页的时候,没有讲到,这里顺带给大家讲一下下拉列表的相关基础知识。

<select name="usertype">
    <option value="学生value">学生text</option>
    <option value="老师value">老师text</option>
    <option value="管理员value">管理员text</option>
    <option></option>
</select>

# ① 获取下拉列表对象

let fm = document.getElementById('myForm');
let usertype = fm.elements['usertype'];//name属性值获取
console.log(usertype);

# ② 下拉列表多选属性 multiple="multiple"(按住键盘CTRL键进行多选),size显示的行数

<select name="usertype" multiple="multiple" size="2">
    <option value="学生value">学生text</option>
    <option value="老师value">老师text</option>
    <option value="管理员value">管理员text</option>
    <option></option>
</select>
//js中处理
usertype.multiple = true;
usertype.size = 2;
//type属性
console.log(usertype.type);//select-one 单行
usertype.multiple = true;
console.log(usertype.type);//select-multiple  多行

# ③ 属性 options(下拉选项的集合)

console.log(usertype.options);
console.log(usertype.options.length);//选择项数量
console.log(usertype.options[0]);//第一个选项对象
//以第一个选项对象为例,看一下常用属性
console.log(usertype.options[0].text);//选项文本
console.log(usertype.options[0].value);//选项值
//最后一个没有value,看一下
console.log(usertype.options[3]);
console.log(usertype.options[3].text);//选项文本
console.log(usertype.options[3].value);//选项值(没有设置value值,则和text值一样)

# ④ 选择选项:选择一项的使用selectedIndex属性,监听的是下拉列表的change事件

//usertype.onchange = function(){}
//usertype.addEventListener('change',function(){},false);
//获取选择的项
usertype.addEventListener('change',function(){
  console.log(this.selectedIndex);//得到当前选项的索引,从0开始

  console.log('当前选择的项的文本', this.options[this.selectedIndex].text);
  console.log('当前选择的项的值', this.options[this.selectedIndex].value);
},false);
//如果是多项选择,它始终返回的是第一个项

//设置选择的项,两种方式
//1.在下拉列表对象上设置,设置下拉列表的项目索引,可以取值和设置值
// usertype.selectedIndex = 2;//定位到某个选项上
//2.设置选项的索引,作用在options上,返回的是布尔值
usertype.options[2].selected = true;

//它们两者的区别:selectedIndex可以取值和设置值,selected返回布尔值可以用作判断
//如:现在选择的是项目的第三项吗?
usertype.onchange = function(){
  if(this.options[2].selected){
    console.log('是选择的第三项')
  }else{
    console.log('不是选择的第三项')
  }
}

# ⑤ 下拉列表动态添加选择项: 使用Option 构造函数,add方法添加选项

let option1 = new Option('超级管理员text','超级管理员value');
// usertype.add(option1,0);//第二个参数设置选择项放置的位置 
//第二个参数说明:
//1. 不填写
usertype.add(option1);//会添加到下拉列表的末尾

# ⑥ 下拉列表动态移除选择项:三种方式(removeChild移除、remove()方法移除和 null 移除)

usertype.removeChild(usertype.options[0]); //DOM 移除
usertype.remove(0); //remove()移除,推荐
usertype.options[0] = null; //null 移除
//当第一项移除后,下面的项,往上顶,所以不停的移除第一项,即可全部移除
usertype.remove(0);
usertype.remove(0);
usertype.remove(0);

# ⑦ 下拉列表动态移动选择项(两个下拉列表选项移动)

<select name="usertype" multiple="multiple" size="5">
    <option value="学生value">学生text</option>
    <option value="老师value">老师text</option>
    <option value="管理员value">管理员text</option>
    <option></option>
</select>
<select name="info" multiple="multiple" size="5">
    <option></option>
</select>

let fm = document.getElementById('myForm');
let usertype = fm.elements['usertype'];//name属性值获取
let info = fm.elements['info'];
usertype.addEventListener('click',function(){
   info.appendChild(this.options[this.selectedIndex]);
},false);

# ⑧ 下拉列表动态排序选择项(选项排序):属性index

let fm = document.getElementById('myForm');
let usertype = fm.elements['usertype'];//name属性值获取
//console.log(usertype.selectedIndex);//作用在下拉列表对象上
//console.log(usertype.options[0].index);//作用在选择项上index

//如何排序
//你要移动谁?比如我要移动:老师text选项
// let option1 = usertype.options[1];//老师text
// usertype.insertBefore(option1,usertype.options[0]);
//参数1:你要移动的选项,参数2:移到谁的前面
//本质还是在操作DOM节点
//usertype.selectedIndex = 0;


//0可以通过什么得到? 
//console.log(option1.index);
//usertype.insertBefore(option1,usertype.options[option1.index - 1]);

//那么就可以动态处理:比如想把 管理员和老师换一下,只需要找到管理员的索引,是2
//let option1 = usertype.options[2];
//usertype.insertBefore(option1,usertype.options[option1.index - 1]);

# ⑨ 获取单选框或者多选框选择的值: 判断checked属性是否为真判断选中的项,defaultChecked判断项目原本设置的默认值

<input type="radio" name="sex" value="男" checked="checked"><input type="radio" name="sex" value="女" ><input type="checkbox" name="loves" value="篮球" checked="checked"> 篮球
<input type="checkbox" name="loves" value="足球" >  足球
<input type="checkbox" name="loves" value="乒乓球" >  乒乓球

let sub = document.getElementById('sub');
sub.onclick = function(e){
  e.preventDefault();
  console.log('点了提交按钮');
   
  console.log('单选按钮选择的值',radioSelect());
  console.log('多选按钮选择的值',checkBoxSelect());
}
//得到单选框选择的值
function radioSelect(){
   let select = '';
   let fm = document.getElementById('myForm');
   let radios = fm.elements['sex'];
   for(let i=0;i<radios.length;i++){ //循环单选按钮
       //if(radios[i].defaultChecked == true){ //遍历每一个找出页面上默认设置了checked属性的选项
       if(radios[i].checked == true){ //遍历每一个找出选中的那个
           select = radios[i].value; //把选中的那个单选按钮值赋值给变量select
           break;//退出for循环,终止后面的循环
       }
   }
   return select;
}

//得到多选框的值
function checkBoxSelect(){
  let select = '';
  let fm = document.getElementById('myForm');
  let checkBox = fm.elements['loves'];
  for(let i=0;i<checkBox.length;i++){ //循环多选按钮
      //if(checkBox[i].defaultChecked == true){ //遍历每一个找出页面上默认设置了checked属性的选项
      if(checkBox[i].checked == true){ //遍历每一个找出选中的那个
          if(select){
              select += ',' +  checkBox[i].value; //把选中的那个单选按钮值赋值给变量select
          }else{
            select = checkBox[i].value;
          }
          //select += ',' +  checkBox[i].value; //把选中的那个单选按钮值赋值给变量select
      }
  }
  return select;
}

//关于这两个函数不需要大家记忆,只需要先理解,后面我们开发的时候,还会在讲到这个知识

# IV、错误与调试

主要了解一下,我们在书写js代码的过程中,如果出现错误,我们改怎么处理,调试它。对于我们初学者,先以了解为主,当你的编程技术慢慢提升以后,你会很渴望掌握这个知识。

# ① 浏览器错误报告

鼠标右键---检查----Console
window.abc();console.log('我是后面的代码!');

# ② 错误处理:try-catch 语句,finally 子句

try { //尝试着执行 try 包含的代码
  window.abc(); //不存在的方法
} catch (e) { //如果有错误,执行 catch,e 是异常对象
  console.log('发生错误啦,错误信息为:' , e); //直接打印调用 toString()方法
  console.log('name:' , e.name);
  console.log('message:' , e.message);
}

console.log('我是后面的代码!');
//try-catch特点:
//1 可以获取错误信息
//2.可以避免浏览器控制台报错
//3.可以屏蔽错误,继续执行(但是继续执行的代码如果和上面错误的语句有上下文关联,继续执行的代码也会出错)


//但是注意:程序一旦执行了catch里面的代码,说明你的try里面的代码写得有问题,后面的程序就不该执行了,因为已经没有意义了
try {
    var b = {};
    //window.abc(); //这里会中断操作
  } catch (e) { 
    console.log('发生错误啦,错误信息为:' , e);
    return false;
  }finally{
      //变量b应该被清空
      //b = null;
      console.log('不管你错不错,我都会执行');
      //处理前面try里面的错误代码,有些乱尾工作
  }
  b = null;
  console.log('我是后面的代码');

# ③ 错误类型

//window.abc();//浏览器给我们抛出了这个错误

//我们知道数据应该写正数,写了负数报错了
//new Array(-1);//RangeError: Invalid array length
//无效的数组长度,RangeError 范围错误

//let box = a; //抛出 ReferenceError(引用)
//错误信息为:ReferenceError: a is not defined(a 是没有定义的)
//ReferenceError 通常访问不存在的变量产生这种错误

//a $ b; //抛出 SyntaxError(语法)
//错误信息为:SyntaxError: missing ; before statement(失踪;语句之前)
//SyntaxError 通常是语法错误导致的

//new 10; //抛出 TypeError(类型 )
//错误信息为:TypeError: 10 is not a constructor(10 不是一个构造函数)
//:TypeError 通常是类型不匹配导致的

//利用上面的错误类型,可以让错误类型更直观一些
try {
  new 10;
} catch (e) {
  if(e instanceof TypeError ){
      console.log('类型错误'+ e.message);
  }else{
      console.log('未知错误'+ e.message);
  } 
}

# ④ try-catch使用场景

不适合使用场景:
1、可以通过修改代码来解决的地方,是不适合用try-catch 的。
2、浏览器兼容性错误,可以通过判断浏览器或者判断这款浏览器是否存在此属性和方法来解决的不适合try-catch。

try {
  let box = document.getElementbyid('box'); //单词大小写错误,导致类型错误
} catch (e) { //这种情况没必要 try-catch
  console.log(e);
}

try {
  document.onclick = function(e){
     console.log(e);//IE不支持
  }
} catch (e) {
  document.onclick = function(){
    console.log(window.event);//IE支持 
  }
}

适合使用的场景:
不到万不得已,无法修改代码的情况下,可能会发生错误才使用try-catch,比如你向服务器提交数据,提交之后由于网络原因、服务器原因可能得不到回应,或者得到错误回应,这个时候就应该使用try-catch。 关于向服务器提交数据,我们后面会有专题讲解,大家先要知道,可以使用try-catch。

# ⑤ 抛出错误

try{
  new 10;
}catch{
   console.log(e);//这叫处理错误,让浏览器不报错了,因为你把错误屏蔽掉了
}

//抛出错误,说明你没办法解决这个错误了,就需要把错误报出来
try{
  new 10;
}catch(e){
  if(e instanceof TypeError ){
      throw new TypeError('类型错误:实例化new的时候,可能产生了错误');
  }else{
      throw new Error('未知错误');
  }
}
//一般情况下,是浏览器自己抛出错误,不需要你手动抛出,做个了解即可

# ⑥ 错误事件error:处理DOM对象产生错误时候使用,比如加载图片失败

let img = document.createElement('img');
img.id = 'testimg';
img.src = '../static/image/logo.png';
document.body.appendChild(img);

let _img = document.getElementById('testimg');
_img.addEventListener('error',function(){
  console.log('图片加载失败');//触发加载失败后,就可以换一张图片替代
});

# ⑦ 错误处理策略

因为js是松散弱类型语言,很多错误的产生是在运行期间的。一般来说,需要关注 3 种错误:1.类型转换错误;2.数据类型错误;3.通信错误

# 1.类型转换错误

console.log(1=='1');//true, 相等对应的是值,类型不比较
console.log(1==='1');//false,全等还需要比较类型

console.log(1 == true);//true,1会隐式转换成布尔值true == true
console.log(1 === true);//false, 1本身是数值,true本身是布尔值,类型就不对了

总结:在类型不相等的情况下,建议使用全等来做判断,当类型是一样的时候,比如都是字符串,就用相等判断即可

# 2.数据类型错误

function test(url){
   let pos = url.indexOf('?');//查找参数中?的位置
   return pos;
}
//正常传值
console.log(test('contact.html?id=1'));

//如果传一个数值就报错
console.log(test(1));

//因此在写代码时候,就需要对参数做一个有效判断,参数必须是字符串
function test(url){
  if(typeof url == 'string'){//typeof url 返回的类型是字符串,因此这里相等即可
    let pos = url.indexOf('?');//查找参数中?的位置
    return pos;
  }else{
    return '数据类型错误';
  }
}

console.log(test('adkdjkdk'));

总结:举上面的例子,是提醒大家,对于某个变量,判断它是字符串、数组、对象、数值的方法,大家记得多少,在写函数判断的时候会不会使用。

# 3.通信错误

关于通信错误,主要是给服务器提交数据的时候,传递的参数有中文,后台处理可能会反生转换乱码,有必要对传递的参数进行字符编码在传递,即我们前面学习的URI 编码方法:encodeURI()、encodeURIComponent()记得使用。

# ⑧ 调试技术和调试工具

  1. 控制台调试
console.error('错误!'); //红色带叉
console.info('信息!'); //白色带信息号
console.log('日志!'); //白色
console.warn('警告!'); //黄色带感叹号
  1. 控制台是js环境,可以直接编写js代码
let box = [2]; Array.isArray(box);
  1. 断点调试(打断点)--- 可以一步一步查看代码执行流程
function box(a,b){
  return a + b;
}

console.log(box(1,2));

记得调试完了之后,取消断点,不然刷新页面又会调试。一般当我们学习第三方库,或者看别人写的代码,看不是很明白某个函数是什么意思的时候,可以打断点,看一下它的执行流程。







# 课程其它章节

# 章节1.课程介绍

# 章节2.javascript基础

# 1、变量

# 2、关键保留字

# 3、语法构成

# 4、数据类型

# 章节3.javascript运算符

# 1、一元运算符

# 2、算术运算符

# 3、赋值运算符

# 4、关系运算符

# 5、逻辑运算符

# 6、字符串、逗号、三元条件运算符

# 7、运算符优先级

# 章节4.流程控制语句

# 1、if语句

# 2、switch 语句

# 3、循环语句

# 4、for...in 语句 枚举对象的属性

# 5、break 和 continue 语句 退出循环语句

# 6、with语句

# 章节5.javascript函数

# 1、函数声明

# 2、函数 return 返回值

# 3、函数的arguments 对象

# 章节6.javascript对象

# 1、创建对象

# 2、对象属性输出、方法(函数)调用

# 3、对象中的方法及应用

# 章节7.javascript数组

# 1、创建及读取数组

# 2、数组中的属性和内置方法:toLocaleString()、valueOf()和 toString()

# 3、数组中的方法:join()、push()、pop()、shift()、unshift()、reverse()、sort()、concat()、slice()、splice()方法

# 4、数组更多操作方法

# 章节8.Date类型:时间和日期

# 1、Date 类型

# 2、格式化日期和时间

# 章节9.Function类型:函数进阶

# 1、函数的声明

# 2、作为值的函数

# 3、函数内部属性

# 4、函数的属性和方法

# 章节10.内置对象:Global、Math对象,变量、作用域和内存问题

# 1、内置对象: Global对象

# 2、内置对象: Math 对象

# 3、变量

# 4、作用域(执行环境)

# 5、 内存问题

# 章节11.匿名函数和闭包

# 1、匿名函数如何自动执行-()()方式

# 2、通过闭包实现局部变量的累加

# 3、循环里的匿名函数取值问题:任何变量都是最后一个值

# 4、闭包中的this对象问题

# 5、 匿名函数私有化:匿名函数自我执行模仿块级作用域,将变量私有化保护数据

# 章节12.javascript基本包装类型

# 1、Number 类型

# ① Number类型静态属性:MAX_VALUE、MIN_VALUE、NaN、NEGATIVE_INFINITY、POSITIVE_INFINITY、prototype
# ② Number类型方法:toString()、toLocaleString()、toFixed()、toExponential()、toPrecision()

# 2、String类型

# ① String类型属性:length、constructor、prototype
# ② String类型通用方法:valueOf()、toLocaleString()和 toString()方法
# ③ String类型方法:charAt(n)、charCodeAt(n)、数组方式截取字符串、concat(str1...str2)、slice(n,m)、substring、substr、indexOf、lastIndexOf、toLowerCase、toUpperCase、toLocaleLowerCase、toLocaleUpperCase、match、search、replace、replaceAll、split、fromCharCode、localeCompare、startsWith、endsWith、includes、trimStart、trimEnd、trim、repeat、魔法字符串

# 3、Array数组的常用方法

# ① 数组中的属性length,数组内置方法toLocaleString()、valueOf()和 toString()
# ② join、push、pop、shift、unshift、reverse、sort、concat、slice、splice
# ③ includes、reduce、find、findIndex、filter、map、forEach
# ④ indexOf、lastIndexOf、at、fill、copyWithin、isArray、es6的扩展运算符、every、some、for...of遍历、keys()、values()、entries()、reduceRight、flat、flatMap、from、of
# ⑤ 集合类型Set()属性方法:size属性、add、has、delete、clear方法,迭代遍历数据:keys()、values()、entries()、for...of遍历,forEach遍历
# ⑥ 小结拓展:获取两个数组交集、并集、差集,获取数组随机元素(验证码)

# 章节13.函数对象数组小结

# ① 对象的方法:for...in(遍历对象)、Object.assign()、Object.create()、Object.freeze()、Object.keys()、Object.values()、Object.entries()
# ② 数组解构:基础用法、省略元素、设置数组元素默认值、嵌套数组(多维数组)的结构赋值、不定元素的解构赋值
# ③ 对象解构:基础用法、重命名解构变量、对象解构时的默认值
# ④ var和let、const的区别
# ⑤ 函数参数默认值:对arguments的影响、对函数length的影响、函数默认表达式、参数默认值的暂时性死区
# ⑥ 函数剩余参数:剩余参数只能出现一个、剩余参数只能放在最后
# ⑦ 展开运算符:针对可迭代对象展开、字面量形式的对象做对象克隆和对象合并

# 章节14.浏览器对象模型BOM及浏览器检测

# ① window 对象:属性和方法、系统对话框、调出打印机print()、网页新建窗口open()、窗口页面的位置:screenX(screenLeft)和 screenY(screenTop)、窗口页面的大小:innerWidth和 innerHeight,outerWidth 和 outerHeight,document.documentElement.clientWidth和document.documentElement.clientHeight
# ② window对象:超时调用:setTimeout()方法、取消超时调用:clearTimeout()方法、间歇调用:setInterval()方法、取消间歇调用:clearInterval()方法、模拟定时器功能,超时调用模拟间歇
# ③ location 对象:获取网址相关信息、assign()方法跳转到指定页面、reload()方法、repalce()方法、获取网址url参数中的键值对、history 对象 : 属性:length,方法:back()、forward()、go(num)
# ④ 获取浏览器相关信息、浏览器插件检测、MIME 类型检测、判断设备是安卓、苹果、是否在微信上面浏览网页、用户查看网页的设备是电脑设备,还是手机设备

# 章节15.网页文档对象模型DOM

# 1、理解DOM

# ① DOM节点树,节点种类:元素节点、文本节点、属性节点

# 2、查找节点信息方法

# ① getElement系列: getElementById()方法,getElementById()方法获取的元素节点属性:tagName、innerHTML等, 获取及设置元素html属性:id,title,style,className等,getElementsByTagName()方法,getElementsByName()方法,getElementsByClassName()方法
# ② querySelector系列: querySelector()方法,querySelectorAll()方法
# ③ 属性操作系列: getAttribute()方法,setAttribute()方法,removeAttribute()方法
# ④ node节点属性: nodeName、nodeType、nodeValue
# ⑤ 层次节点属性: childNodes属性,firstChild 和 lastChild 属性,ownerDocument 属性,parentNode 属性,previousSibling 属性,nextSibling 属性,忽略空白文本节点,attributes 属性

# 3、操作节点

# ① document.write()方法,createElement()方法,appendChild()方法,createTextNode()方法, insertBefore()方法,模拟在指定节点的后面添加一个节点,repalceChild()方法,cloneNode()方法,removeChild()方法

# 4、DOM 类型、扩展、操作内容

# ① DOM 类型: Document类型补充(获取html节点对象,获取body节点对象,获取文档声明:DOCTYPE对象,document.title,document.images,document.links等),Text类型补充(normalize()方法,splitText(num)方法,deleteData删除字符,insertData插入字符,replaceData替换字符,substringData获取字符)
# ② DOM 扩展: scrollIntoView(), children 属性,contains()方法
# ③ DOM操作网页内容:innerText、innerHTML、outerText、outerHTML

# 5、DOM实战:操作表格

# ① DOM创建表格
# ② HTML DOM 来操作表格: 获取表格元素对象, 获取表格的标题对象<caption>,获取表头表尾<thead>、<tfoot>、<tbody>,获取表格的行数,获取表格主体里的行数,获取表格主体内第一行的单元格数量(tr),获取表格主体内第一行第一个单元格的内容(td),删除标题、表头、表尾、行、单元格,HTML DOM 创建一个表格

# 6、操作CSS样式

# ① 行内样式style的获取、赋值、移除属性removeProperty,计算后的样式获取(行内、内联、外联样式):window 对象下getComputedStyle()方法

# 7、操作页面样式

# ① className关键字设置样式,创建函数:hasClass() 判断是否存在某个类名,addClass() 如果不存在的这个类名,添加这个类名,removeClass() 如果存在的这个类名,删除这个类名

# 8、操作CSS外联样式表.css文件

# ① 获取CSSStyleSheet,外联的css样式表对象

# 9、DOM元素尺寸(元素大小)和位置(元素位置)

# ① 获取元素 CSS 大小回顾:通过 style 内联获取元素大小,通过计算getComputedStyle()方法获取元素大小,通过 CSSStyleSheet 对象中的 cssRules属性获取元素大小(需将网页放到服务器上查看)
# ② 获取元素实际大小:clientWidth 和 clientHeight:获取可视区的元素大小,可以得到元素内容及内边距所占据的空间大小,scrollWidth 和 scrollHeight:获取滚动内容的元素大小,offsetWidth 和 offsetHeight:获取元素大小,包含边框、内边距和滚动条
# ③ 获取元素周边大小位置:clientLeft 和 clientTop:获取元素设置了左边框和上边框的大小;offsetLeft 和 offsetTop:获取当前元素相对于父元素的位置;scrollTop 和 scrollLeft:这组属性可以获取滚动条被隐藏的区域大小(滚动条滚动高度宽度),也可设置定位到该区域(定位滚动条); getBoundingClientRect()方法:返回一个矩形对象,包含四个属性:left、top、right和 bottom,分别表示元素各边与页面上边和左边的距离

# 10、动态加载脚本

# ① 动态加载js文件,动态加载样式表

# 章节16.事件

# 1、事件基础知识

# ① 事件处理函数列表
# ② 内联模型
# ③ 脚本模型

# 2、事件处理函数分类

# ① 鼠标事件:click,dblclick,mousedown,mouseup,mouseover,mouseout,mousemove
# ② 键盘事件:keydown,keypress,keyup
# ③ HTML事件(包括表单事件):load,unload,resize,scroll,select,change,input,submit,reset,focus,blur

# 3、事件对象

# ① this 关键字和上下文
# ② 获取事件对象
# ③ 获取鼠标按钮(左键、右键、中间滚轮)
# ④ 可视区及屏幕坐标:clientX,clientY,screenX,screenY,鼠标点击位置距离页面顶部距离(带滚动条)
# ⑤ 修改键:shiftKey对应Shfit 键,ctrlKey对应 Ctrl 键,altKey 对应 Alt 键,metaKey 对应 windows 键,判断是否按下了它们
# ⑥ 键盘事件:键码(键盘上的任意键)keyCode,字符编码(键盘上可以输出字符的键)charCode
# ⑦ 事件对象的常用属性和方法:target,事件冒泡,取消:e.stopPropagation()
# ⑧ 事件对象其他属性方法:relatedTarget(在 mouseover 和 mouseout 事件中获取),e.preventDefault(),contextmenu取消鼠标右击弹出的菜单,鼠标滚轮:mousewheel

# 4、事件绑定

# ① 剖析传统事件绑定问题:同名事件后者覆盖前者,this 传递问题
# ② 现代事件绑定处理函数:添加事件addEventListener(),删除事件removeEventListener(),设置冒泡和捕获

# 章节17.表单处理及错误处理与调试

# 1、表单基础知识

# ① 获取form对象的方法
# ② 提交表单:默认提交form表单按钮type="submit",js中表单提交及阻止提交,让没有提交功能的按钮(其它元素)完成表单提交使用的是form.submit()方法,实现使用键盘 CTRL + enter(回车键)提交数据
# ③ 预防表单数据重复提交,表单重置:按钮重置type="reset",js重置 form.reset()方法

# 2、使用HTML DOM获取表单控件元素

# ① elements属性获取表单控件元素集合
# ② 多个表单字段都使用同一个 name,如单选框多选框,常用属性
# ③ 表单共有字段方法:foucs(),blur(),表单共有的字段事件:blur,change,input,focus

# 3、表单中的文本框

# ① input.value 和 textarea.value 获取输入的内容,defaultValue获取默认设置的value值
# ② 选择文本:使用 select()方法,可以将文本框里的文本选中,并且将焦点设置到文本框中
# ③ 选择部分文本:setSelectionRange()方法,这个方法接受两个参数:索引和长度
# ④ select 事件,选中文本框文本后触发
# ⑤ 获取选择的文本,两个属性:selectionStart 和 selectionEnd
# ⑥ 文本过滤输入(表单验证,文本验证,输入框验证)
# ⑦ 剪切事件:cut,复制事件:copy,粘贴事件:paste,禁用输入法
# ⑧ 利用正则表达式将用户输入的非数字字符替换成空,只留下数字
# ⑨ 输入框自动切换焦点到下一个输入框(类似Tab键),maxlength属性可输入的最大字符长度

# 4、表单中的下拉列表(选择框)

# ① 获取下拉列表对象
# ② 下拉列表多选属性 multiple="multiple"(按住键盘CTRL键进行多选),size显示的行数
# ③ 属性 options(下拉选项的集合)
# ④ 选择选项:选择一项的使用selectedIndex属性,监听的是下拉列表的change事件
# ⑤ 下拉列表动态添加选择项: 使用Option 构造函数,add方法添加选项
# ⑥ 下拉列表动态移除选择项:三种方式(removeChild移除、remove()方法移除和 null 移除)
# ⑦ 下拉列表动态移动选择项(两个下拉列表选项移动)
# ⑧ 下拉列表动态排序选择项(选项排序):属性index
# ⑨ 获取单选框或者多选框选择的值: 判断checked属性是否为真判断选中的项,defaultChecked判断项目原本设置的默认值

# 5、错误与调试

# ① 浏览器错误报告,错误处理:try-catch 语句,finally 子句,错误类型
# ② try-catch使用场景,抛出错误,错误事件error:处理DOM对象产生错误时候使用,比如加载图片失败,错误处理策略,调试技术和调试工具

# 章节18.数据Cookie、XML、JSON

# 1、Cookie、sessionStorage、localStorage

# ② 封装cookie(创建,获取,删除)
# ③ sessionStorage、localStorage

# 2、XML

# ① 创建 XMLDOM 对象(document.implementaion,DOMParser类型创建XMLSerializer类型进行序列化成字符串),解析错误
# ② 加载读取外部xml文件
# ③ XPath操作XML
# ④ 封装XPath操作xml文件

# 3、JSON

# ①JSON 语法,可以表示三种类型的值
# ② 获取外部json文件
# ③ 解析json字符串(将json字符串转成数组对象——json结构数据):JSON.parse()方法
# ④ js原生值数组、对象转成json字符串--JSON.stringify()
# ⑤ toJSON



# 其它学期课程

# 第一学期(学习顺序:01)

第一学期课程专为零基础的学员定制录制的,纯html+css做企业网站的网页,主讲html和css的相关基础知识,flex布局相关知识,封装css基础样式库,引入字体图标及网页开发基础布局思维,完成企业网站网页的开发过程。

[第一学期学习视频]

# 第二学期【第1季】(学习顺序:02)

主讲JavaScript的基础,建议所有学员观看。
[第1季学习文档] [第1季学习视频]

# 第二学期【第2季】(学习顺序:03)

JavaScript中的面向对象,类,ajax,封装js库过渡到jQuery, vue.js基础配置网站页面,建议所有学员观看。
[第2季学习文档] [第2季学习视频]

# 第二学期【第3季】(学习顺序:04)

egg.js基础,响应式网页布局,Bootstrap框架,响应式后台系统管理,完整企业网站前后台开发,建议所有学员观看。
[第3季学习文档] [第3季学习视频]

# 第二学期【第4季】(学习顺序:05)

主要对第三季,同学们开发的企业网站,进行一个完整的上线运维流程的一个讲解,同学们将网站开发完成之后,如何进行上线运维,将项目交付给客户。
[第4季学习文档] [第4季学习视频]

更新时间: 2024年11月19日星期二中午11点54分