您当前的位置:首页 > 网站建设笔记 >

原生JS级联所在地区下拉选择框代码兼容IE8+

0
或者固定电话
建议填写常用名称家里父母家公司

检测数组
自从ECMAScript 3做出规定以后,就出现了确定某个对象是不是数组的经典问题。对于一个网页,或者一个全局作用域而言,使用instanceof操作符就能得到满意的结果if(value instanceof Array){//对数组执行某些操作}。instanceof操作符的问题在于,它假定单一的全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。为了解决这个问题,ECMAScript 5新增了Array.isArray()方法。这个方法的目的是最终确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。这个方法的用法是if(Array.isArray(value)){//对数组执行某些操作}。支持Array.isArray()方法的浏览器有IE9+、Firefox 4+、Opera 10.5+和Chrome。

if(typeof Array.isArray==="undefined"){
  Array.isArray=function(arg){return Object.prototype.toString.call(arg)==="[object Array]";}
}

转换方法
所有对象都具有toLocaleString()、toString()和valueOf()方法。其中,调用数组的toString()方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。而调用valueOf()返回的还是数组。实际上,为了创建这个字符串会调用数组每一项的toString()方法。

操作方法
ECMAScript为操作已经包含在数组中的项提供了很多方法。其中,concat()方法可以基于当前数组中的所有项创建一个新数组。具体来说,这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给concat()方法传递参数的情况下,它只是复制当前数组并返回副本。如果传递给concat()方法的是一或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中。如果传递的值不是数组,这些值就会被简单地添加到结果数组的末尾。

下一个方法是slice(),它能够基于当前数组中的一或多个项创建一个新数组。slice()方法可以接受一或两个参数,即要返回项的起始和结束位置。在只有一个参数的情况下,slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。注意,slice()方法不会影响原始数组。

如果slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。例如,在一个包含5项的数组上调用slice(-2,-1)与调用slice(3,4)得到的结果相同。如果结束位置小于起始位置,则返回空数组。

function convertToArray(nodes){
  var array=null;
  try{
//Array.prototype.slice.call(arguments)能将具有length属性的对象转换成数组,除了IE下的节点集合(因为ie下的dom对象是以com对象的形式实现的,js对象与com对象不能进行转换)
    array=Array.prototype.slice.call(nodes);
  }catch(e){
    array=new Array();
    for(var i=0,len=nodes.length;i<len;i++){
      array[i]=nodes[i];
    }
  }
  return array;
}

相关介绍:Array.prototype.slice.call()

补充
将函数的实际参数转换成数组的方法
方法一、var args=Array.prototype.slice.call(arguments);
方法二、var args=[].slice.call(arguments);
方法三、var args=[];for(var i=0,len=arguments.length;i<len;i++){args.push(arguments[i]);}
方法四、const args=Array.from(arguments);
对参数使用slice会阻止某些JavaScript引擎中的优化(比如 V8)。如果你关心性能,尝试通过遍历arguments对象来构造一个新的数组。另一种方法是使用被轻视的Array构造函数作为一个函数:
var args=(arguments.length===1 ? [arguments[0]] : Array.apply(null, arguments));

迭代方法
ECMAScript 5为数组定义了5个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。根据使用的方法不同,这个函数执行后的返回值可能会也可能不会影响方法的返回值。以下是这5个迭代方法的作用。

  • every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。
  • filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。
  • forEach():对数组中的每一项运行给定函数,这个方法没有返回值(本质上与使用for循环迭代数组一样)。
  • map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
  • some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。

以上方法都不会修改数组中的包含的值。在这些方法中,最相似的是every()和some(),它们都用于查询数组中的项是否满足某个条件。这些数组方法通过执行不同的操作,可以大大方便处理数组的任务。支持这些迭代方法的浏览器有IE9+、Firefox 2+、Safari 3+、Opera 9.5+和Chrome。

<script src="address-cn.js"></script>
<script type="text/javascript">
(function(){

if(typeof Array.isArray==="undefined"){
  Array.isArray=function(arg){return Object.prototype.toString.call(arg)==="[object Array]";}
}

function convertToArray(nodes){
  var array=null;
  try{
    array=Array.prototype.slice.call(nodes);
  }catch(e){
    array=new Array();
    for(var i=0,len=nodes.length;i<len;i++){
      array[i]=nodes[i];
    }
  }
  return array;
}

if(!Array.prototype.forEach){
  Array.prototype.forEach=function(callback,thisArg){
    var T,k;
    if(this==null){throw new TypeError("this is null or not defined");}
//首先不能保证this一定是一个数组, 所以先转化为一个对象以保证可以进行for in循环
//for-in语句是一种精准的迭代语句, 可以用来枚举对象的属性。for-in语句的语法for(property in expression) statement
//for(var propName in window){document.write(propName);}
//在这个例子中, 我们使用for-in循环来显示了BOM中window对象的所有属性。每次执行循环时, 都会将window对象中存在的一个属性名赋值给变量propName。这个过程会一直持续到对象中的所有属性都被枚举一遍为止。与for语句类似, 这里控制语句中的var操作符也不是必需的。但是, 为了保证使用局部变量, 我们推荐上面例子中的这种做法。ECMAScript对象的属性没有顺序。因此, 通过for-in循环输出的属性名的顺序是不可预测的。具体来讲, 所有属性都会被返回一次, 但返回的先后次序可能会因浏览器而异。但是, 如果表示要迭代的对象的变量值为null或undefined, for-in语句会抛出错误。ECMAScript 5更正了这一行为, 对这种情况不再抛出错误, 而只是不执行循环体。为了保证最大限度的兼容性, 建议在使用for-in循环之前, 先检测确认该对象的值不是null或undefined。
    var O=Object(this);
//这里位操作符可以将非数字转化为0, 将数字转化为整数
    var len=O.length >>> 0;
    if(typeof callback!=="function"){throw new TypeError(callback+" is not a function");}
    if(arguments.length > 1){
      T=thisArg;
    }
    k=0;
    while(k < len){
      var kValue;
      if(k in O){
        kValue=O[k];
        callback.call(T,kValue,k,O);
      }
      k++;
    }
  };
}

if(!Array.prototype.every){
  Array.prototype.every=function(fun,thisArg){
    "use strict";
    if(this === void 0 || this===null)throw new TypeError();
    var t=Object(this);
    var len=t.length >>> 0;
    if(typeof fun!=="function")throw new TypeError();
    var thisArg=arguments.length>=2 ? arguments[1] : void 0;
    for(var i=0;i<len;i++)
    {
      if(i in t && !fun.call(thisArg, t[i], i, t))return false;
    }
    return true;
  };
}

function getElementsByClassName(className,tag,parent){
  if(typeof parent=="string"){parent=document.getElementById(parent);}
  parent=parent||document;
  if(parent.getElementsByClassName){
    return parent.getElementsByClassName(className);
  }else{
    var allTags=parent.getElementsByTagName(tag);
    var matchingElements=new Array();
    className=className.replace(/\-/g,"\\-");
    var regex=new RegExp("(^|\\s)"+className+"(\\s|$)");
    var element;
    for(var i=0;i<allTags.length;i++){
      element=allTags[i];
      if(regex.test(element.className)){matchingElements.push(element);}
    }
    return matchingElements;
  }
}

var addrname=getElementsByClassName("addr-alias","span")[0].getElementsByTagName("a");
addrname=convertToArray(addrname);
addrname.forEach(function(item,index,array){
  if(item.addEventListener){
    item.addEventListener("click",function(event){
      event.preventDefault();
      event.stopPropagation();
      document.forms["addform"].elements["addressname"].value=this.innerHTML;
    },false);
  }else{
//IE实现了与DOM中类似的两个方法attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。由于IE8及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。

//在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域。在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行,在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window。
    item.attachEvent("onclick",function(event){
      event.returnValue=false;
      event.cancelBubble=true;
      document.forms["addform"].elements["addressname"].value=item.innerHTML;//这里不能使用this.innerHTML
    });
  }
});

/*
for(var i=0,len=addrname.length;i<len;i++){
  addrname[i].onclick=function(num){
    return function(eventObject){
      document.forms["addform"].elements["addressname"].value=addrname[num].innerHTML;
    }
  }(i);
}
*/

function clearSelectbox(selectbox){
  for(var len=selectbox.options.length;len>=0;len--){
    selectbox.remove(len);
  }
  return selectbox;
}

var each=function(object,callback){
  var type=(function(){
    switch(object.constructor){
      case Object:return "Object";break;
      case Array:return "Array";break;
      case NodeList:return "NodeList";break;
      default:return "null";break;
    }
  })();
  if(type==="Array"||type==="NodeList"){
    [].forEach.call(object,function(v,i){
      return callback.call(v,i,v)===false?false:true;
    });
  }else if(type==="Object"){
    for(var i in object){
      if(callback.call(object[i],i,object[i])===false){break;}
    }
  }
}
var provinceEle=document.forms["addform"].elements["province"];
var cityEle=document.forms["addform"].elements["city"];
var zipEle=document.forms["addform"].elements["zip"];

each(address.provinces,function(k,v){
  var newOption=new Option(k,k);
  provinceEle.add(newOption,undefined);
});

provinceEle.onchange=function(eventObject){
  var selectedOption=this.options[this.selectedIndex];
  var selectedValue=selectedOption.value;
  clearSelectbox(cityEle).add(new Option("市",""),undefined);
  clearSelectbox(zipEle).add(new Option("区/县",""),undefined);
  if(selectedValue){
    each(address.provinces[selectedValue],function(k,v){
      var newOption=new Option(k,k);
      cityEle.add(newOption,undefined);
    });
  }
}

cityEle.onchange=function(eventObject){
  var l=provinceEle.options[provinceEle.selectedIndex].value;
  var n=this.options[this.selectedIndex].value;
  clearSelectbox(zipEle).add(new Option("区/县",""),undefined);
  if(n){
    each(address.provinces[l][n],function(k,v){
      var newOption=new Option(v,v);
      zipEle.add(newOption,undefined);
    });
  }
}

})();
</script>
顶部中部底部