分类目录归档:web

微信小程序 开发教程 第四课

感谢大家支持!博卡君周末休息了两天,今天又回到战斗状态了。上周五晚上微信放出官方工具和教程了,推荐程序猿小伙伴们都去试一试,结合教程和代码,写写自己的 demo 也不错。

闲话不多说,开始更新!

第七章:微信小程序编辑名片页面开发

编辑名片有两条路径,分为新增名片流程与修改名片流程。

用户手填新增名片流程:



首先跳转到我们的新增名片页面 1 需要传递用户的当前 userId,wx.navigateTo 带值跳转。Manual 为 true 设置用户走的是新增路线。

新增名片页面 1 基本布局如下:

取到 userId。

使用微信自带的 input 组件验证也非常好用,如 maxLength 属性,可以限制用户输入长度,如我这的姓名长度是最大 5 位,直接数字 5 即可。

也可以自定义一些验证效果,具体可以根据需求进行一些验证配置,取到用户输入的值,进行操作。

这里绑定了自带的模态框提示组件。

其中 modalHidden2 是模态框开关。

另外 proptText 是需要提示的内容。

即使很多输入框也支持数据动态改变,非常方便。

实际效果,非常快捷,比以前省去很多事情,编写小程序,发现最大的好处可能就是我们不必去考虑一系列兼容性问题。

最后还有个头像上传图片,测试了下目前上传到后台服务器还有点问题,应该是内测版本不太完善导致吧。

设置的直接是背景图片。

提交表单与跳转。

提交表单使用的是自带的 bindsubmit 事件组件,在 button 组件上添加 formType=”submit” 即可,还有点需注意的是使用表单提交功能时 input 需加上 name 属性,这个传递方式是以键值对的形式传递的。

这时候跳转到编辑页 2 页面,这个页面是根据用户填写的手机号码识别到匹配的公司,页面非常简单,一个数据循环而已,单选框日后可能还需要美化一下。

同样也是一些数据绑定以及验证效果。

实际渲染效果可以看到。

这个和第一个编辑页面逻辑基本不相上下,一些基本验证与提交,这里就讲到前面两步骤即可,编辑页面3也是同理,这里不再过多啰嗦。

修改名片流程效果图与需求,修改名片是一次性把以前填写的个人信息全部渲染出来,供用户来改动:

名片图片模块,上传图片暂时还有点问题,这里就是模仿了个跳转组件,比较建议需要跳转的页面还是使用 wx.navigateTo 控制好一点,wx.navigateTo 提供了给我们不同的 3 个跳转路由,封装的都很好,而且跳转页面很多牵连到传值之类的,可以达到统一管理也可以避免一些看不到的 bug 吧,总之还是根据业务需求来定:

姓名手机必填模块:

个人信息模块,直接循环(block)出来:

Onload 时我们请求必填与选填数据:

  • requiredGroup 必填中文信息
  • notRequiredGroup 选题中文信息
  • requiredGroupEn 必填英文信息
  • notRequiredGroupEn 选题英文信息
//请求名片对应的公司的中文信息的属性组数据,分为必填和选填
//选题项变量以no开头
        requester.getOfflineCardInfoGroupFields(userId, cardId,
            function (res) {
                //debugger
                var userName = res.card.userName;
                var mobile = res.card.mobile;
                var requiredGroup = res.requiredGroupCh;
                var notRequiredGroup = res.notRequiredGroupCh;
                var requiredGroupEn = res.requiredGroupEn;
                var notRequiredGroupEn = res.notRequiredGroupEn;
                var reqLen = requiredGroup.fields.length;
                var nreqLen = notRequiredGroup.fields.length;
                var reqLenEn = requiredGroupEn.fields.length;
                var nreqLenEn = notRequiredGroupEn.fields.length;
                self.setData({
                    userName: userName,
                    mobile: mobile,
                    requireFields: requiredGroup.fields,
                    notRequireFields: notRequiredGroup.fields,
                    requireFieldsEn: requiredGroupEn.fields,
                    notRequireFieldsEn: notRequiredGroupEn.fields,
                    l1: reqLen,
                    l2: nreqLen + reqLen,
                    l3: reqLenEn + nreqLen + reqLen
                });
                self.forceUpdate();
            }, function (code, msg) {
                console.info("code=" + code + "&msg=" + msg);
            });

中英文信息必填与选填渲染:

这里表单提交数据转换有点复杂(大家根据业务需求来做,不必花时间研究这里的方法),获取的是数组,按照后台需要的数据格式进行转换传递过去。

今天再回去理下首页 A、B、C 定点跳转功能实现方法。

首先是右边小索引布局以及数据绑定,数据绑定和名片夹列表上的字母一样,该字母下面有名片则渲染出来,没有则不需要渲染,id同样是当前字母与右边显示的内容一样:

数据 sort,和 group.name 数据一样:

这里是因为 # 不支持设为 id(就是 id=”#”),故而进行了一个转化。

点击事件:获取到当前 ID,以及绑定数据 toView 为当前 ID。

首先名片列表,名片上的字母索引都在 scroll-view 里面,这个 scroll-view 必须设置好固定的高度,设置成 100% 与 100vh 是无效的,y 轴的滚动开关打开,scroll-into-view 需要跳转到它子元素的 id 上。

可以看下:

这个 group.name==sortmsg,等于就是 A==A,B==B 同理。


在这里如顶部有些菜单栏的话,你就要注意好布局了,不然会出现向下偏移这个菜单栏的高度,其实你只要和字母索引同级下即可避免此问题(这里的顶部菜单以模板分离出去了,分离模板的时候需注意下,需要在这里绑定模板的一些数据会出现失效,具体没有继续深入研究下去)。

跳转功能基本实现(ohter 就是 # 底部)。

好了,今天更新到这里,下一篇我们聊聊「微信小程序分组功能开发及其它小功能完善」。
微信小程序 开发教程 第一课
微信小程序 开发教程 第二课
微信小程序 开发教程 第三课

微信小程序 开发教程 第五课
微信小程序 开发教程 第六课

微信小程序 开发教程 第三课

感谢朋友们的支持!这两天博卡君收到了很多支持和安慰,也认识了很多志同道合的朋友。目前微信公布的工具和代码都不是正式版,随时有可能调整,大家先体验和学习为主吧。最近这个教程搞得我也心力交瘁了,虽然苦逼,但很开心。今天一口气更新两章,周末给自己放松一下好好睡一觉,大家慢慢研究吧!

 

第五章:微信小程序名片夹详情页开发

今天加了新干货!除了开发日志本身,还回答了一些朋友的问题。
闲话不多说,先看下「名片盒」详情页的效果图:

 

备注下大致需求:顶部背后是轮播图,二维码按钮弹出模态框信息、点击微信栏、点击存入手机,地址栏需要地图展示,名片分享也是模态框指引。
首先是轮播图,autoplay 自动播放,interval 轮播的时间,duration 切换速度,可以根据自己需求去添加。
Delete:是删除按钮,加载进来是隐藏的,需用户点轮播图进去后,轮播图全屏显示才出来。
noClickImg 与 ClickImg:切换全屏与非全屏轮播图,绑定了点击事件 changeClick 来切换,只是改变样式即可。
Block:图片列表。
Number_img:当前轮播 index(currentNumber),与图片 length 集合(cardnum)。
其中 currentNumber:

//轮播图发生改变时改变数字

//初始化数据

Data:{

currentNumber:1

}

slidechange:function(e){

var number = e.detail.current;

this.setData({

currentNumber:number+1

})

},


这里可以看到全屏状态下当关闭按钮被点击后 getBackStyle,把 changeClick 切换到 imgFullScrenn 待命。

再次点击返回原样式,

切换后事件又走回到 getBackStyle 了,灵活运用。

刷新下开发者工具可以看到具体效果如下:

详情页可以看到信息基本都是样式一样,可以使用微信提供的循环 block。
下面是详情页里面的个人信息数据,

如果有信息就显示出来,没有数据的不显示,这里使用

 

//中文信息

var chinaMessage = res.card.groups[0].fields;

var personMessage= []

for(var i = 0;i<chinaMessage.length;i++){

personMessage.push(chinaMessage[i])

 

}

//为空或者null是不显示判断

for(var k in personMessage){

if(personMessage[k].value==null || personMessage[k].value==””){

personMessage[k][“display”] = “none”;

}else{

personMessage[k][“display”] = “block”;

}

}

具体以 json 数据格式来处理,我们需要做的就是给它绑定 display 的值,然后我们调用即可。

微信此版本的 setData 不支持异步更新数据,故而我们在发生真实网络数据请求时一定要在后面加上 forceUpdata(),强制触发视图渲染,否则会出现很多莫名其妙的 bug。

这里说明下:如是服务器真实数据。

可以看到会报错,可能是 js 的执行顺序,依次往下走,此时网络数据还在请求中。

定义一个变量即可。

当然这里的数据都是 push 上来的。

以下是二维码弹出信息。

这是弹出模态框二维码信息,布好局初始化是none状态。那里需要它直接绑定数据即可:

This.setData({

//模态框名字:”显示?隐藏”

})

方法是让它显示。

需要用他的地方调用方法即可。(支持重复调用)

详情页公司位置地图直接调用微信提供的接口实现(群里的 demo 有实现方式)。

可以看下实现的效果:


下一章:微信(小程序)名片盒我的页面开发。

 

第六章:微信小程序「名片盒」「我」的页面开发

效果图与需求:


用户有多张名片,需要左右切换查看,往下切换是菜单按钮。
这里需求两处滑动,用到了微信提供给我们的滑动组件 swiper,并且进行了嵌套使用,第一层是名片展示与菜单按钮的上下滑动,第二层是名片展示的左右滑动(支持互相嵌套使用的,可以放心使用)。
Vertical 加上就是纵向滑动,去掉即是左右滑动。
整体结构如下所示:

点击事件绑定的是数据切换方式,因为需要支持多次点击切换。

初始化数据是 nextSlide:

再看下 nextSlide 事件。currentSlide 是当前页面的 index,改变它即可完成切换效果,可以看上图初始化数据时设置了 cs 是 0。

因而赋值当前 data.cs+1 即可,再把绑定点击事件 clickNext 切换成 nextSlideAgain。

再看下 nextSlideAgain 事件,执行的减去 1 个索引,实现多次点击切换效果。

具体效果可以看到。

点击个人名片进去编辑名片页面,由于需要带参,故而使用的是 wx.navgateTo。

可以看下效果:


最后上点干货:

我们发出第一篇教程的时候有人就注意到这点了,怎么做真实数据交互,下面大家可以了解下。

首先进去是 MD5 加密,requster 交互层。

怎么引用 MD5.js?当然是模块化 require,被引用的 js 不要忘记 module.exports 出来。

下面是 requester.js 引用 MD5.js。

ApplicationRoot 是服务器地址(配置服务器时在开发设置页面查看 AppID 和 AppSecret,配置服务器域名)。

Require.js 这里 module.exports 是暴露方法出去。

这时候在全局 app.js 里面引入 require.js 映射到全局 global,这个 global 是全局的。

这时候那个页面需要那个页面就直接去接受吧,模块化是不是很好用?

可以完整的看下与后台做数据交互的一个请求实现方式如下:
图一是 requester.js 里面的封装。

图二是需要调用数据的页面渲染。

大家有什么疑问可以多指点,会在下一章说明一些已知疑问。

应该有一些人喜欢 sublime 编辑器,有人问怎么切换高亮,在你的右下角直接切换成 html 即可。

微信小程序 开发教程 第一课
微信小程序 开发教程 第二课

微信小程序 开发教程 第四课
微信小程序 开发教程 第五课
微信小程序 开发教程 第六课

 

微信小程序 开发教程 第二课

今天一波三折,承受了超出预料的压力和煎熬,最后还是决定继续放出我的更新教程。我想我一没有泄露公司的代码,二没有提供泄露开发工具下载,只是从程序猿角度写了篇开发日志。我已经做好了最坏的准备,就算放弃这份工作,也会把完成的教程交给「名片盒」的团队继续更新发布下去!做了就绝不后悔!只是博卡君水平有限,文章还请各位多包容。

以上!开始吧:

小提示:

http://wxopen.notedown.cn/

这里面复刻了微信小程序的 api

第三章:微信小程序项目结构以及配置

找到创建的 demo 文件夹,把项目导入到你的编辑器,这里使用的是 Sublime Text 编辑器。

这个时候需要根据自己的项目需求结构进行更改了,项目根目录下面是首页渲染的几个 tabBar 页面,以及 app 的一些配置文件,如名片盒项目的 tabBar 是 3 个切换菜单:

我们先找到 app.json 文件打开配置好这几个菜单,配置好 tabBar,这个直接把配置文件改成你自己设计的即可。

App.json 里面有几个配置项:

  • Pages:这个是编写的 js 文件,后缀 .js 这里不需要使用,配置好正确路径即可正常应用到(应用不到在重启微信开发者工具会直接报 page 错误)。
  • Window:配置顶部的一些样式,文档介绍比较详细。
  • tabBar:底部的几项配置,见名知意。
  • networkTimeout:暂时没发现用处,建议看文档。
  • 根据实际项目需求进行添加与更改。
  • iconPath 和 selectedIconPath:底部菜单按钮图片与得到切换点击高亮。
  • “text”:可以去掉,全部去掉会发现底部 tabar 高度会减少很多。

Json 文件配置好后,根据项目进行文件创建,

Demo:存放的是假数据,这一期的开发工具支持 require,假数据使用的是 .js 文件形式,

里面的数据结构 json 一致,把 data 暴露出去即可。

然后取数据 require 进来即可,这一点使用很方便;

Images:图片路径;

Page:除 tabar 以外的页面;

Servise:服务交付层(与后台联调真实数据时使用);

Wxss:一些公共的 css 文件。

看到这里大家发现每个页面都被连带好三个不同的后缀。分别页面,css,js 目前只能依照这样,是微信应用号的一个规范吧。

Wxss 文件是引入你写的样式文件,也可以直接在里面写样式。

Js 文件需全部配置到 pages 里面才能生效。

下一章:微信小程序首页面开发。

第四章:微信小程序首页面开发

进行了各种准备与配置后,来到首页开发。首先需要实现首页效果图如下:

Template 名片很多,需要用模板。

这里需要微信提供的基础组件大致是 input(搜索框)、

action-sheet(右边是个底部下拉菜单,需要下拉菜单)、

Scroll-view (右边 ABC 跳转)、(这个目前实现还有点问题,正在攻克中)。

View 是块元素,整个搜索框的一个样式。

  • 名片夹:由于该项目主打名片功能,故很多地方使用,所以需要把名片以 template 分离出来。
  • Template:定义一个模板,name 模板的名字其实是个作用域。
  • Block:循环控制,名片很多,必须用循环出来,和很多操作数据的前端框架循环差不多。
  • 支持自定义属性 data,这里用作判断线上名片以及线下名片。

View 里面是一些数据引入,里面是支持三目运算符。

引入 template 时非常方便,is 和 name 一样,data 是 nameData 传递过来的数据填充。

一切都绑定数据为中心点。

取到数据具体操作根据你数据结构:

这里的数据结构和 json 数据结构一样,

这里如要传到页面的话即是

this.setData({

nameData:card_list_name.data.cards,

timeData:card_list_time.data.cards

});

因为页面遍历的是 nameData,timeData

可以看下打印出来的数据结构,根据你的结构进行解析与传递。

也可以看下这里对数据的一些操作。(这里须根据定义的 json 数据格式来操作的)

名片的样式由于很多页面需要使用放在 common.css 里面,这个 common.css 是所有页面都需要用到,一些初始化设置。它是在 pp.wxss 里面引用之后才能被映射到全局 APP。

搜索框:其中 bindChange 为输入框发生改变事件。微信提供的 bindchange 在支持方面还有小问题,目前是失去焦点才能触发到此事件的发生,待后续完善吧,先实现功能再说。

bindInputChange:function(e){

//发生搜索事情

var self = this; //this绑定,这个this指向微信的提供window

var Text = e.detail.value.toUpperCase();//取到输入的内容

if(Text==""){   //如果输入为空一些东西需要显示否则不显示

show_letter ="block";

}else{

        show_letter= "none";

}

this.setData({

        show_letter:show_letter,

        showSheet:true

});

var res = nameData;   获取到传递的数据

if(data_type=="name"){

}else if(data_type=="time"){

        res= timeData;

};

for(var k in res){  //for-in循环取到data里面的cards

var data =res[k].cards;

for(var i =0;i<data.length;i++){  //循环取到需要搜索的关键字对比

If(data[i].userName!=null&& data[i].userName.indexOf(Text)!=-1){

data[i]["display"]= "block"//存在就是赋值显示

}else{

data[i]["display"]= "none"; // 不存在赋值不显示

}

}

}

}

菜单栏:做到菜单栏,使用微信提供的下拉菜单组件 action-sheet,它被触发的条件在这里。

一切以绑定事件为起点:

bindButtonTapSheet:function(e){

//调取底部下拉菜单栏

}

还是得先布好局才能被调动。

Js 配置:

Data 初始化数据:

这里得取非,直接设置 false 调不出来: 调用事件。

调出来还得去掉它啊:如下相同即可

取消直接上事件即可。(分为菜单栏外部与底部)

//好了,就是这么简单。实现效果简单,体验效果确实非常不错。

还需要个 loading 效果(暂时没做动画,后期再考虑。)

Loading 布局

首页的最外层 view

根据微信的生命周期

Onload:function(e){

this.setData({

toastDisplay:”block”,

htmlWrapDisplay:”none”

})

},

onShow:function(e){

this.setData({

toastDisplay:”none”,

htmlWrapDisplay:”block”

})

}

加载条完成。

扫一扫,直接调用拍照功能,从这里看到微信提供的拍照 api 使用起来非常快速,只需根据需求配置即可。

点击扫一扫之后,在开发者工具即可看到如下效果。

做到这里说明下,dom 长度有限制,页面的结构太长,也是无法渲染的,暂且把公司排序暂时先去掉了。

左边的 ABC 跳转(还在继续完善中)。

这里还有个左滑删除名片功能,微信没有提供这个在移动端很实用的功能真的比较遗憾,后面得花点时间自己写下(后续完善)。

好了,今天更新的内容就先到这里。后续的内容「名片盒」的服务号会按照约定帮我每日更新发布出来。大家每天可以进到这个服务号输入「应用号」、「小程序」或者「教程」,能获取教程更新列表。

感谢「名片盒」团队的信任和全力支持。

博卡君之所以选择这个服务号动手术,是因为从服务号角度它更像一个 APP 化的工具,相比我们公司在做的媒体性服务号,更有工具化改造价值:里面包括调用扫描、检索、分享、积分等复杂功能。感兴趣的朋友可以对照现在的服务号和开发教程印证。

 

好了,不罗嗦了。教程会在「名片盒」和开源中国保持更新,QQ 群就当做大家相互交流学习的平台吧,有兴趣的朋友也可以来。

相关教程
微信小程序 开发教程 第一课
微信小程序 开发教程 第三课
微信小程序 开发教程 第四课
微信小程序 开发教程 第五课
微信小程序 开发教程 第六课

微信小程序(应用号)开发新闻客户端的实战课程

下载最新版的微信小程序开发工具,目前是v0.9.092300

下载地址:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html

官方文档:https://mp.weixin.qq.com/debug/wxadoc/dev/index.html

git下载地址:http://git.oschina.net/dotton/news

先看下效果图:

输入图片说明

一、新建应用

1.内测阶段对于无内测号的开发者,请点无AppId。

2.然后选择一个本地目录作为工程目录。

输入图片说明

3.项目名称任意,设置好目录,勾上当前目录创建quick start项目。如图:

输入图片说明

4.点击添加项目,这时可以运行的效果。是自己的微信个人信息以及一HelloWorld文本框。

5.右边是调试窗口,有2个警告,是由于没有AppID导致的,可以暂时忽略,不影响开发。

输入图片说明

6.提示一下,在app.json中设置debug:true,这样控制台看到实时的交互信息,以及将来在js文件中设置断点,类似与Chrome的调试工具以及Firefox的Firebug。

关于首页配置:

{
  "pages":[
    "pages/index/index",
    "pages/logs/logs"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle":"black"
  },
  "debug":true
}

其中pages属性表明每一个页面的存在,其中第一条为首页,即pages/index/index

二、请求网络API接口

1.前提条件:

这里需要用到聚合数据的新闻接口,前往:https://www.juhe.cn/docs/api/id/235 注册、申请接口,拿到key,我这里已经申请到一个key:482e213ca7520ff1a8ccbb262c90320a,可以直接拿它做测试,然后就可以将它集成到自己的应用了。

2.使用微信小程序接口来访问网络:

改写index.js文件:

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    motto: 'Hello World',
    userInfo: {}
  },
  //事件处理函数
  bindViewTap: function() {
    wx.navigateTo({
      url: '../logs/logs'
    })
  },
  onLoad: function () {
  // 访问聚合数据的网络接口
  wx.request({
    url: 'http://v.juhe.cn/toutiao/index',
    data: {
     type: '' ,
     key: '482e213ca7520ff1a8ccbb262c90320a'
    },
    header: {
        'Content-Type': 'application/json'
    },
    success: function(res) {
      console.log(res.data)
    }
  })

    console.log('onLoad')
    var that = this
    //调用应用实例的方法获取全局数据
    app.getUserInfo(function(userInfo){
      //更新数据
      that.setData({
        userInfo:userInfo
      })
    })
  }
})

3.查看效果,检查Console控制台,得到以下信息:

输入图片说明

说明已经成功取得到了数据。

三、将json格式的数据渲染到视图

这里要用到swipe组件实现大图轮播,文档见:https://mp.weixin.qq.com/debug/wxadoc/dev/component/swiper.html

1.清空原index.wxml内容,加入如下代码:

<swiper indicator-dots="true"
  autoplay="true" interval="5000" duration="1000">
  <block wx:for="{{topNews}}">
    <swiper-item>
      <image src="{{item.thumbnail_pic_s02}}" class="slide-image" width="355" height="150"/>
    </swiper-item>
  </block>
</swiper>

2.相应地在index.js文件的onLoad方法中加入如下代码来获取网络数据

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    topNews:[],
    techNews:[]
  },
  onLoad: function () {
    var that = this
  // 访问聚合数据的网络接口-头条新闻
  wx.request({
    url: 'http://v.juhe.cn/toutiao/index',
    data: {
     type: 'topNews' ,
     key: '482e213ca7520ff1a8ccbb262c90320a'
    },
    header: {
        'Content-Type': 'application/json'
    },
    success: function(res) {
      if (res.data.error_code == 0) {
        that.setData({
        topNews:res.data.result.data
        })
      } else {
        console.log('获取失败');
      }
    }
  })

  }
})

3.看到轮播已经成功的展示出来了

输入图片说明

4.依样画葫芦,同样操作读取列表新闻:

<view class="news-list">
  <block wx:for="{{techNews}}">
    <text class="news-item">{{index + 1}}. {{item.title}}</text>
  </block>
</view>

配合样式表,不然列表文字排版是横向的,将以下css加到index.wxss中:

.news-list {
  display: flex;
  flex-direction: column;
  padding: 40rpx;
}
.news-item {
  margin: 10rpx;
}

输入图片说明

  1. 继续美化,文字列表也采用缩略图+大标题+出处+日期的形式

输入图片说明

样式表与布局文件
index.wxss

/**index.wxss**/
.news-list {
  display: flex;
  flex-direction: column;
  padding: 40rpx;
}

.news-item {
  display: flex;
  flex-direction: row;
  height:200rpx;
}

.news-text {
  display: flex;
  flex-direction: column;
}

.news-stamp {
    font-size: 25rpx;
    color:darkgray;
    padding: 0 20rpx;
    display: flex;
    flex-direction: row;
    justify-content:space-between;
}

.news-title {
  margin: 10rpx;
  font-size: 30rpx;
}

.container {
  height: 5000rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  /*padding: 200rpx 0;*/
  box-sizing: border-box;
}

.list-image {
  width:150rpx;
  height:100rpx;
}

index.wxml

<!--index.wxml-->
<swiper indicator-dots="true"
  autoplay="true" interval="5000" duration="1000">
  <block wx:for="{{topNews}}">
    <swiper-item>
      <image src="{{item.thumbnail_pic_s02}}" mode="aspectFill" class="slide-image" width="375" height="250"/>
    </swiper-item>
  </block>
</swiper>
<view class="container news-list">
  <block wx:for="{{techNews}}">
    <view class="news-item">
      <image src="{{item.thumbnail_pic_s}}" mode="aspectFill" class="list-image"/>
      <view class="news-text">
        <text class="news-title">{{item.title}}</text>
        <view class="news-stamp">
          <text>{{item.author_name}}</text>
          <text>{{item.date}}</text>
        </view>
      </view>
    </view>
  </block>
</view>

四、跳转详情页与传值

保存当前点击的新闻条目信息中的title,参见官方文档:https://mp.weixin.qq.com/debug/wxadoc/dev/framework/view/wxml/event.html

传值到详情页

<!--logs.wxml-->
<view class="container">
  <text class="news-title">{{title}}</text>
  <text class="news-info">暂时找不到WebView的组件接口,于是不能加载网页数据</text>
</view>
  //事件处理函数
  bindViewTap: function(event) {
    wx.navigateTo({
      url: '../detail/detail?title='+event.currentTarget.dataset.newsTitle
    })
  }

//index.js
   //事件处理函数
  bindViewTap: function(event) {
    wx.navigateTo({
      url: '../detail/detail?title='+event.currentTarget.dataset.newsTitle
    })
  }
<!--index.wxml-->
//加入data-xxx元素来传值
<view class="container news-list">
  <block wx:for="{{techNews}}">
    <view class="news-item" data-news-title="{{item.title}}" bindtap="bindViewTap">
      <image src="{{item.thumbnail_pic_s}}" mode="aspectFill" class="list-image"/>
      <view class="news-text">
        <text class="news-title">{{item.title}}</text>
        <view class="news-stamp">
          <text>{{item.author_name}}</text>
          <text>{{item.date}}</text>
        </view>
      </view>
    </view>
  </block>
</view>

当然也可以通过获取全局的变量的方式传值,这里场景是由一个页面与子页面是一对一传值关系,所以不推荐,可参考quickStart项目中微信个人信息的传值方式来做。
app.js末尾加上

  globalData:{
    userInfo:null,
    newsItem:null
  }
})

输入图片说明

由于未在官方文档中找到WebView的组件,所以详情的网页正文暂时无法实现。

结语

整体开发过程还是比较舒适的,上手难度不高,过程中用到一定的CSS语法,本质上还是体现了一个H5开发模式,WXML实质上一种模板标签语言。
git下载地址:
http://git.oschina.net/dotton/news

八大排序算法(冒泡,简单选择,直接插入,快速排序,希尔排序,归并排序,堆排序,基数排序)之代码实现(js&php版本) 

前言

从学习数据结构开始就接触各种算法基础,但是自从应付完考试之后就再也没有练习过,当在开发的时候也是什么时候使用什么时候去查一下,现在在学习JavaScript,趁这个时间再把各种基础算法整理一遍,分别以JS和PHP语法的方式编写代码。

冒泡排序

原理:临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位(因为较大的/较小的 总是交换到了右边,当到最后一位时,他肯定是最大的/最小的),然后再从头开始进行两两比较交换,直到倒数第二位时结束
时间复杂度:平均情况:O(n2) 最好情况:O(n) 最坏情况:O(n2)
空间复杂度:O(1)
稳定性:稳定

       //JavaScript语法
       var array = [23,0,32,45,56,75,43,0,34];

       for(var i = 0; i < array.length; i++)
       {
           var isSort = true;
           for(var j = 0; j < array.length - 1 - i; j++)
           {
               if(array[j] > array[j+1])
               {
                   isSort = false;
                   var temp = array[j];
                   array[j] = array[j + 1];
                   array[j + 1] = temp;
               }
           }
           //如果一次交换都没有产生,就说明数据已经是排过序的,可以直接退出。
           if(isSort)
           {
               break;
           }
       }
       console.log(array);    
 <?php
         $array = [23,0,32,45,56,75,43,0,34];
 
         for($i = 0; $i < count($array); $i++)
         {
             $isSort = true;
             for($j = 0; $j < count($array) - 1; $j++)
             {
                 if($array[$j] > $array[$j+1])
                 {
                     $isSort = false;
                     $temp = $array[$j];
                     $array[$j] = $array[$j + 1];
                     $array[$j + 1] = $temp;
                 }
             }
             if($isSort)
             {
                break;
             }
         }
         var_dump($array);
 ?>

简单选择排序

原理:通过n-i次关键字之间的比较,从n-i+1 个记录中选择关键字最小的记录,并和第i(1<=i<=n)个记录交换。简单说就是分成左右两堆,左堆排过序的,右堆未排序的,从未排序的右堆中找出最小的,放到已排序的左边,形成新的两堆,直到最后排序完成。
简单选择排序的性能要略优于冒泡排序
时间复杂度:平均情况:O(n2) 最好情况:O(n) 最坏情况:O(n2)
空间复杂度:O(1)
稳定性:不稳定

//JavaScript
        var array = [23,0,32,45,56,75,43,0,34];

        for(var i = 0; i < array.length - 1; i++)
        {
            var pos = i;
            for(var j = i + 1; j < array.length;j++)
            {
                if(array[j] < array[pos])
                {
                    pos=j;
                }
            }
            var temp=array[i];
            array[i]=array[pos];
            array[pos]=temp;
        }
        console.log(array);
 ```

 ```php
 <?php
         $array = [23,0,32,45,56,75,43,0,34];
         for($i = 0; $i < count($array); $i++)
     {
         $pos = $i;
         for($j = $i + 1;$j < count($array); $j++)
         {
             if($array[$j] < $array[$pos])
             {
                 $pos = $j;
             }
         }
         $temp = $array[$i];
         $array[$i] = $array[$pos];
         $array[$pos] = $temp;
     }
     var_dump($array);
 
 ?>

直接插入排序

原理:将一个记录插入到已排序好的有序表中,从而得到一个新的记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。
这个和平时打牌很像,左边是已排好序的,右边是未排序的。从右边第一个时,一个个拿,拿到后插入到左边已排序的正确位置上,直到排完。
比冒泡法和选择排序的性能要更好一些。
时间复杂度:平均情况:O(n2) 最好情况:O(n) 最坏情况:O(n2)
空间复杂度:O(1)
稳定性:稳定

//JavaScript
var array = [23,0,32,45,56,75,43,0,34];
 for(var j = 0;j < array.length;j++) {
     var key = array[j];
     var i = j - 1;
     while (i > -1 && array[i] > key)
     {
         array[i + 1] = array[i];
         i = i - 1;
     }
     array[i + 1] = key;
 }
 console.log(array);
<?php
    //直接插入排序
        $array = [23,0,32,45,56,75,43,0,34];
        for($i = 0; $i < count($array); $i++)
    {
        $key = $array[$i];
        $j= $i - 1;
        while($j > -1 && $array[$j] > $key)
        {
            $array[$j +1] = $array[$j];
            $j = $j - 1;
        }
        $array[$j + 1] = $key;
    }
    var_dump($array);
?> 

快速排序

原理:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。实际开发中用得最多的
时间复杂度:平均情况:O(nlog2n) 最好情况:O(nlog2n) 最坏情况:O(n2)
空间复杂度:O(nlog2n)
稳定性:不稳定

 //JavaScript 快速排序

 var array = [23,0,32,45,56,75,43,0,34];
 var quickSort = function(arr) {
    if (arr.length <= 1) { return arr; }//检查数组的元素个数,如果小于等于1,就返回。
    var pivotIndex = Math.floor(arr.length / 2);//
    var pivot = arr.splice(pivotIndex,1)[0];//选择"基准"(pivot),并将其与原数组分离,
    var left = [];//定义两个空数组,用来存放一左一右的两个子集
    var right = [];
    for (var i = 0; i < arr.length; i++)//遍历数组,小于"基准"的元素放入左边的子集,大于基准的元素放入右边的子集。
      {
          if (arr[i] < pivot) {
              left.push(arr[i]);
          } else {
              right.push(arr[i]);
          }
      }
 
      return quickSort(left).concat([pivot], quickSort(right));//使用递归不断重复这个过程,就可以得到排序后的数组。
  };
  var newArray=quickSort(array);
  console.log(newArray);
 <?php
                $array = [23,0,32,45,56,75,43,0,34];
         function quick_sort($arr) {
             //先判断是否需要继续进行
             $length = count($arr);
             if($length <= 1) {
                 return $arr;
             }
         
             $base_num = $arr[0];//选择一个标尺  选择第一个元素
 
             //初始化两个数组
             $left_array = array();//小于标尺的
             $right_array = array();//大于标尺的
             for($i=1; $i<$length; $i++) {            //遍历 除了标尺外的所有元素,按照大小关系放入两个数组内
                 if($base_num > $arr[$i]) {
                     //放入左边数组
                     $left_array[] = $arr[$i];
                 } else {
                     //放入右边
                     $right_array[] = $arr[$i];
                 }
             }
             //再分别对 左边 和 右边的数组进行相同的排序处理方式
             //递归调用这个函数,并记录结果
             $left_array = quick_sort($left_array);
             $right_array = quick_sort($right_array);
             //合并左边 标尺 右边
             return array_merge($left_array, array($base_num), $right_array);
         }
                $newArray=quick_sort($array);
                var_dump($newArray);
 ?>

希尔排序

原理:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。。
时间复杂度:平均情况:O(n√n) 最好情况:O(nlog2n) 最坏情况:O(n2)
空间复杂度:O(1)
稳定性:不稳定

javaScript  希尔排序
 var array = [23,0,32,45,56,75,43,0,34];
 var shellSort = function (arr)
 {
     var length=arr.length;
     var h=1;
     while(h<length/3)
     {
         h=3*h+1;//设置间隔
     }
     while(h>=1)
     {
         for(var i=h; i<length; i++)
         {
             for(var j=i; j>=h && arr[j]<arr[j-h]; j-=h)
             {
                 var temp =arr[j-h];
                 arr[j-h]=arr[j];
                 arr[j]=temp;
             }
         }
         h=(h-1)/3;
     }
     return arr;
 }
 var newArray = shellSort(array);
 console.log(newArray);
 <?php
 //希尔排序
         $array = [23,0,32,45,56,75,43,0,34];
         function shellSort($arr)
         {
             $length=count($arr);
             $h=1;
             while($h<$length/3)
             {
                 $h=3*$h+1;//设置间隔
             }
             while($h>=1)
             {
                 for($i=$h; $i<$length; $i++)
                 {
                     for($j=$i; $j>=$h && $arr[$j]<$arr[$j-$h]; $j-=$h)
                     {
                          $temp =$arr[$j-$h];
                          $arr[$j-$h]=$arr[$j];
                          $arr[$j]=$temp;
                     }
                 }
                 $h=($h-1)/3;
             }
             return $arr;
         }
         $newArray = shellSort($array);
         var_dump($newArray)
 ?>

归并排序

原理:假设初始序列含有n个记录,则可以看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到(不小于n/2的最小整数)个长度为2或1的有序子序列,再两两归并,…如此重复,直至得到一个长度为n的有序序列为止
时间复杂度:平均情况:O(nlog2n) 最好情况:O(nlog2n) 最坏情况:O(nlog2n)
空间复杂度:O(1)
稳定性:稳定

 //JavaScript 归并排序
         function isArray1(arr){
             if(Object.prototype.toString.call(arr) =='[object Array]'){
                 return true;
             }else{
                 return false;
             }
         }
         function merge(left,right){
             var result=[];
             if(!isArray1(left)){
                 left = [left];
             }
             if(!isArray1(right)){
                 right = [right];
             }
             while(left.length > 0&& right.length >0){
                 if(left[0]<right[0]){
                     result.push(left.shift());
                 }else{
                     result.push(right.shift());
                 }
             }
             return result.concat(left).concat(right);
         }
 
         function mergeSort(arr){
             var len=arr.length;
             var lim ,work=[];
             var i,j,k;
             if(len ==1){
                 return arr;
             }
             for(i=0;i<len;i++){
                 work.push(arr[i]);
             }
             work.push([]);
             for(lim=len;lim>1;){//lim为分组长度
                 for(j=0,k=0;k<lim;j++,k=k+2){
                     work[j]=merge(work[k],work[k+1]);
                 }
                 work[j]=[];
                 lim=Math.floor((lim+1)/2);
             }
             return work[0];
         }
         var array = [23,0,32,45,56,75,43,0,34];
         
         console.log(mergeSort(array));
<?php  
      //归并排序
       function mergeSort(&$arr) {
            $len = count($arr);//求得数组长度
         
            mSort($arr, 0, $len-1);
        }
        //实际实现归并排序的程序
        function mSort(&$arr, $left, $right) {
         
            if($left < $right) {
                //说明子序列内存在多余1个的元素,那么需要拆分,分别排序,合并
                //计算拆分的位置,长度/2 去整
                $center = floor(($left+$right) / 2);
                //递归调用对左边进行再次排序:
                mSort($arr, $left, $center);
                //递归调用对右边进行再次排序
                mSort($arr, $center+1, $right);
                //合并排序结果
                mergeArray($arr, $left, $center, $right);
            }
        }

        //将两个有序数组合并成一个有序数组
        function mergeArray(&$arr, $left, $center, $right) {
            //设置两个起始位置标记
            $a_i = $left;
            $b_i = $center+1;
            while($a_i<=$center && $b_i<=$right) {
                //当数组A和数组B都没有越界时
                if($arr[$a_i] < $arr[$b_i]) {
                    $temp[] = $arr[$a_i++];
                } else {
                    $temp[] = $arr[$b_i++];
                }
            }
            //判断 数组A内的元素是否都用完了,没有的话将其全部插入到C数组内:
            while($a_i <= $center) {
                $temp[] = $arr[$a_i++];
            }
            //判断 数组B内的元素是否都用完了,没有的话将其全部插入到C数组内:
            while($b_i <= $right) {
                $temp[] = $arr[$b_i++];
            }
         
            //将$arrC内排序好的部分,写入到$arr内:
            for($i=0, $len=count($temp); $i<$len; $i++) {
                $arr[$left+$i] = $temp[$i];
            }
         
        }

        $arr = array(23,0,32,45,56,75,43,0,34);
        mergeSort($arr);
        var_dump($arr);
?>

堆排序

原理:堆排序就是利用堆进行排序的方法.基本思想是:将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是堆顶 的根结点.将它移走(其实就是将其与堆数组的末尾元素交换, 此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素的次大值.如此反复执行,便��得到一个有序序列了
时间复杂度:平均情况:O(nlog2n) 最好情况:O(nlog2n) 最坏情况:O(nlog2n)
空间复杂度:O(1)
稳定性:不稳定

//JavaScript  堆排序    
      var array = [23,0,32,45,56,75,43,0,34];
       function heapSort(array)
       {
           for (var i = Math.floor(array.length / 2); i >= 0; i--)
           {
               heapAdjust(array, i, array.length - 1); //将数组array构建成一个大顶堆
           }
           for (i = array.length - 1; i >= 0; i--)
           {
               /*把根节点交换出去*/
               var temp = array[i];
               array[i] = array[0];
               array[0] = temp;
               /*余下的数组继续构建成大顶堆*/
               heapAdjust(array, 0, i - 1);
           }
           return array;
       }

       function heapAdjust(array, start, max)
       {
           var temp = array[start];//temp是根节点的值
           for (var j = 2 * start; j < max; j *= 2)
           {
               if (j < max && array[j] < array[j + 1])
               {  //取得较大孩子的下标
                   ++j;
               }
               if (temp >= array[j])
                   break;
               array[start] = array[j];
               start = j;
           }
           array[start] = temp;
       }
       var newArray = heapSort(array);
       console.log(newArray);
 
<?php
    //堆排序
    function heapSort(&$arr) {
        #初始化大顶堆
        initHeap($arr, 0, count($arr) - 1);
        
        #开始交换首尾节点,并每次减少一个末尾节点再调整堆,直到剩下一个元素
        for($end = count($arr) - 1; $end > 0; $end--) {
            $temp = $arr[0];
            $arr[0] = $arr[$end];
            $arr[$end] = $temp;
            ajustNodes($arr, 0, $end - 1);
        }
    }
    
    #初始化最大堆,从最后一个非叶子节点开始,最后一个非叶子节点编号为 数组长度/2 向下取整
    function initHeap(&$arr) {
        $len = count($arr);
        for($start = floor($len / 2) - 1; $start >= 0; $start--) {
            ajustNodes($arr, $start, $len - 1);
        }
    }
    
    #调整节点
    #@param $arr    待调整数组
    #@param $start    调整的父节点坐标
    #@param $end    待调整数组结束节点坐标
    function ajustNodes(&$arr, $start, $end) {
        $maxInx = $start;
        $len = $end + 1;    #待调整部分长度
        $leftChildInx = ($start + 1) * 2 - 1;    #左孩子坐标
        $rightChildInx = ($start + 1) * 2;    #右孩子坐标
        
        #如果待调整部分有左孩子
        if($leftChildInx + 1 <= $len) {
            #获取最小节点坐标
            if($arr[$maxInx] < $arr[$leftChildInx]) {
                $maxInx = $leftChildInx;
            }
            
            #如果待调整部分有右子节点
            if($rightChildInx + 1 <= $len) {
                if($arr[$maxInx] < $arr[$rightChildInx]) {
                    $maxInx = $rightChildInx;
                }
            }
        }
        
        #交换父节点和最大节点
        if($start != $maxInx) {
            $temp = $arr[$start];
            $arr[$start] = $arr[$maxInx];
            $arr[$maxInx] = $temp;
            
            #如果交换后的子节点还有子节点,继续调整
            if(($maxInx + 1) * 2 <= $len) {
                ajustNodes($arr, $maxInx, $end);
            }
        }
    }
    
    $arr = array(23,0,32,45,56,75,43,0,34);
    heapSort($arr);
    var_dump($arr);
?>        

基数排序

原理:将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
时间复杂度:平均情况:O(d(r+n)) 最好情况:O(d(n+rd)) 最坏情况:O(d(r+n)) r:关键字的基数 d:长度 n:关键字个数
空间复杂度:O(rd+n)
稳定性:稳定

<?php
      #基数排序,此处仅对正整数进行排序,至于负数和浮点数,需要用到补码,各位有兴趣自行研究
      
      #计数排序
      #@param $arr 待排序数组
      #@param $digit_num 根据第几位数进行排序
      function counting_sort(&$arr, $digit_num = false) {
          if ($digit_num !== false) { #如果参数$digit_num不为空,则根据元素的第$digit_num位数进行排序
              for ($i = 0; $i < count($arr); $i++) {
                  $arr_temp[$i] = get_specific_digit($arr[$i], $digit_num);
              } 
          } else {
              $arr_temp = $arr;
          }
  
          $max = max($arr);
          $time_arr = array(); #储存元素出现次数的数组
  
          #初始化出现次数数组
          for ($i = 0; $i <= $max; $i++) {
              $time_arr[$i] = 0;
          }
  
          #统计每个元素出现次数
          for ($i = 0; $i < count($arr_temp); $i++) {
              $time_arr[$arr_temp[$i]]++;
          }
  
          #统计每个元素比其小或相等的元素出现次数
          for ($i = 0; $i < count($time_arr) - 1; $i++) {
              $time_arr[$i + 1] += $time_arr[$i];
          }
  
          #利用出现次数对数组进行排序
          for($i = count($arr) - 1; $i >= 0; $i--) {
              $sorted_arr[$time_arr[$arr_temp[$i]] - 1] = $arr[$i];
              $time_arr[$arr_temp[$i]]--;
          }
  
          $arr = $sorted_arr;
          ksort($arr);    #忽略这次对key排序的效率损耗
      }
  
      #计算某个数的位数
      function get_digit($number) {
         $i = 1;
         while ($number >= pow(10, $i)) {
            $i++;
         }

         return $i;
      }
  
      #获取某个数字的从个位算起的第i位数
      function get_specific_digit($num, $i) {
         if ($num < pow(10, $i - 1)) {
             return 0;
         }
         return floor($num % pow(10, $i) / pow(10, $i - 1));
      }
  
      #基数排序,以计数排序作为子排序过程
      function radix_sort(&$arr) {
          #先求出数组中最大的位数
          $max = max($arr);
          $max_digit = get_digit($max);
  
          for ($i = 1; $i <= $max_digit; $i++) {
              counting_sort($arr, $i);
          }   
      }
  
  
      $arr = array(23,0,32,45,56,75,43,0,34);
      radix_sort($arr);
  
      var_dump($arr);
?>    

JavaScript正则表达式详解

在JavaScript中,正则表达式由RegExp对象表示。RegExp对象呢,又可以通过直接量和构造函数RegExp两种方式创建,分别如下:

//直接量
var re = /pattern/[g | i | m];
//构造函数
var re = new RegExp(["pattern", ["g" | "i" | "m"]]);

其中,末尾的可选字符(g、i和m)分别表示:

  g模式执行一个全局匹配。简而言之,就是找到所有匹配,而不是在找到第一个之后就停止。

  i模式执行不区分大小写的匹配。

  m: 多行模式,^和$锚除了匹配字符串的开头和结尾外,还匹配每行的开头和结尾。例如,模式/Java$/m匹配”Java”和”JavanScript”。

基础篇

特殊字符

在正则表达式中,所有的字母字符和数字都可以按照直接量与自身匹配,如/JavaScript/匹配的就是字符串”JavaScript”,但是有些特殊字符呢?如换行符。所以在JavaScript中规定以反斜杠()开头的转义序列支持这些特殊字符。常用的特殊字符如下:

转义字符 匹配
n 换行符
r 回车
f 换页符
t 制表符
v 垂直制表符

 字符类–

在正则表达式中,倘若将单独的字符放入方括号([ ])中,就可以组合成字符类。应用到匹配字符串中,我们可以将其看成一个漏斗,当字符串中的每个字符通过它时,都查找是否在这个类里面,如若在,就匹配成功,否则out。如下:

/*
    match为字符串的方法,它的唯一参数就是一个正则表达式,
    如果该正则表达式设置了标志g,该方法返回的数组包含的就是出现在字符串中的所有匹配。
    详细的用法将在下面“正则表达式在String中的应用”细讲
*/
"abc".match(/[abc]/g);

匹配结果为:

如果我们的意愿是,想匹配除字符a、b、c之外的字符呢?我们可以定义一个否定类,只需将^符号放入[ ]中作为开头就OK啦。如下:

"abc".match(/[^abc]/g);

由于某些字符类经常用到,固JavaScript的正则表达式就用反斜杠()与一些特殊字符组合起来表示这些常用类,而不必再需要我们自行添加,如d。

常用正则字符类如下:

字符类 匹配 例子
[ …] 位于方括号之中的任意字符 /M[onke]y/ 匹配 “Moy”
[ ^…] 除包含在方括号之中的任意字符 /M[^onke]y/ 匹配 “May”
. 除换行符之外的任意字符 /../ 匹配 “Mo”
w 字母、数字或下划线 /1w/ 匹配 “1A”
W 除字母、数字和下划线之外的字符 /1W/ 匹配 “1%”
s 单个空白字符 /MsK/ 匹配 “M K”
S 单个非空白字符 /MSK/ 匹配 “M_K”
d 0到9的数字 /d/ 匹配 “1”
D 非数字 /D/ 匹配 “M”

重复匹配–

当我们需要匹配三位数字时,我们可以这样:/ddd/,但是当我们需要匹配10位或者更多时呢?考虑到这一点,正则表达式为我们提供了重复字符{ n, m },表示匹配前一项至少n次,但是不能超过m次。例如,刚才我们所说的匹配三位数字时,我们可以利用重复字符这样啦:/d{3}/。

由于某些重复类型经常用到,so,正则规定一些特殊字符表示这些重复类型。

正则重复字符,详情见下:

字符 含义 例子
 {n, m} 匹配前一项至少n次,但不能超过m次 /d{2,3}/ 匹配”12″
{n, } 匹配前一项至少n次,或者更多 /d{2, }/ 匹配”123″
{n} 匹配前一项恰好n次 /d{2}/ 匹配”12″
匹配前一项0次或者1次,等价于{0,1} /d?/ 匹配”2″
+ 匹配前一项1次或者多次,等价于{1, } /d+/ 匹配”12″
* 匹配前一项0次或者多次,等价于{0, } /d*/ 匹配”12″

另,以上重复字符重复规则为:尽可能多的匹配,即俗称的“贪婪匹配”,如:”aaaa”.match(/a+/);匹配的就是整个字符串”aaaa”,而不是匹配到第一个字符a时,就放弃匹配。

那么,有所谓的”贪婪匹配”,就有”非贪婪匹配”,它的规则嘛,肯定与”贪婪匹配”相反咯,即:尽可能少的匹配。

那么,怎么才能触发非贪婪模式呢?

只需要在重复字符后加入?,就ok啦,如({1, 4}?、+?等),如”aaaa”.match(/a+?/);就只会匹配首个字符a咯。

注意,是尽可能少的匹配,而不是少的匹配哦。

神马意思?如下:

"aaab".match(/a*b/);
"aaab".match(/a*?b/);

!匹配结果都是”aaab”!

有没有点诧异,为什么”aaab”.match(/a*?b/);的匹配结果会是”aaab”,而不是”ab”呢?

那是因为正则���配都是从左往右的,就”aaab”.match(/a*?b/);而言,当遇到首字符a时,它会继续往下匹配,直到能符合匹配模式/a*?b/为止,这就是为什么说是尽可能少的匹配,前提是满足匹配规则

如”abbb”.match(/ab*?/)的匹配结果就是”a”啦。

字符 |( )(?: …)

1.1、字符” | ” 用于分隔,表示或。

什么意思?

举个栗子,如/ab | cd | ef/就可以匹配字符串”ab”或者”cd”或者”ef”。

是不是和字符类[ ]很像啊?

是的,如/a | b | c/和/[abc]/匹配效果是一样的哦。

But字符类[ ]仅针对单个字符而言,而分隔字符” | “涉及更广,可以针对多个字符而言,如上述所说的/ab | cd | ef/,字符类就不行咯。

你可能会说,如果我想对利用” | “组装的类进行多次匹配呢?

加个括号就是啦。如:

/(ab | cd |ef)+/

好滴,说到括号,我们再来看看它的作用。非常强大哦。

————————————–分割线 ————————————–

Linux 基础入门教程—-正则表达式基础   http://www.linuxidc.com/Linux/2015-08/121441.htm

Linux正则表达式sed 详述  http://www.linuxidc.com/Linux/2015-04/116309.htm

Linux正则表达式特性及BRE与ERE的区别 http://www.linuxidc.com/Linux/2014-03/99152.htm

grep使用简明及正则表达式 http://www.linuxidc.com/Linux/2013-08/88534.htm

正则表达式的用法 http://www.linuxidc.com/Linux/2013-03/81897.htm

正则表达式之零宽断言 http://www.linuxidc.com/Linux/2013-03/81897.htm

Linux中正则表达式与文件格式化处理命令(awk/grep/sed) http://www.linuxidc.com/Linux/2013-03/81018.htm

基础正则表达式 http://www.linuxidc.com/Linux/2014-09/106296.htm

常用正则表达式整理 http://www.linuxidc.com/Linux/2014-10/108076.htm

————————————–分割线 ————————————–

1.2、括号”( )”

括号的作用如下:

  1、我们可以将一个单独的项目组合成一个子表达式,以便我们可以用|、*等来处理它。如,上诉所示的/(ab | cd | ef)+/

  2、利用括号括起来的部分,我们可以在正则表达式的后面引用前面用括号括起来的子表达式的匹配结果,注意是结果,而不是括起来的正则表达式。

针对第二点,有什么用呢?如我们有个需求,我想匹配在单引号或者双引号中的数字(’12345’)时,我们就可轻而易举利用这第二点,写好正则表达式,如下:

/(['"])d*1/

测试结果如下:

好了,就第二点作用而言,结合上述demo,我们再来看看它的具体引用法则吧:

—-以反斜杠加数字的方式,引用前面带括号的子表达式,而这个数字呢指的就是第几个子表达式,计算规则为从左往右,计算遇到的左括号” ( “,到想引用的地方位置为止,无论在括号中还嵌套不嵌套括号。

测试Demo如下:

咦,倘若我只想让括号的作用为分组,而不想在后面计入引用呢?毕竟括号多了,不好计算呢。

那么,我们就来看看字符(?: …)咯。

1.3、(?: …)

(?: …)的作用就是,规定括号只用于分组,而不计入后面的引用,不好理解,看个demo就明白啦。如下:

/(Java(?:Script))(nice)/

如果我想在末尾引用子表达式nice,那么是2,而不是3咯,因为用(?: …)来分组滴,只管分组,而不引用,切记切记。

对(?: …)的测试demo如下:

 

匹配位置–

在前面我们提到,创建正则对象时,可选字符m表示:多行模式,^和$锚除了匹配字符串的开头和结尾外,还匹配每行的开头和结尾。

那么这个^和$就是正则为我们提供的匹配位置,即所谓的

例如:

将/JavaScript/变为/^JavaScript/,就只匹配字符串中开头为JavaScript的啦,如匹配”JavaScriptxxx”中的JavaScript,而不匹配”xxxJavaScript”中的JavaScript。

正则表达式中的锚字符详情见下:

字符 含义
^ 匹配字符串的开头
$ 匹配字符串的结尾
b 匹配一个词语的边界,指[a-zA-Z_0-9]之外的字符
B 匹配非词语边界位置
(? = p) 正前向声明,exp1(?=exp2),匹配后面是exp2的exp1
(? ! p) 反前向声明,exp1(?!exp2),匹配后面不是exp2的exp1

^和$好理解,但是b、(?=)、(?!)可能比较陌生,结合上表,我们再来看看下面的demo就好啦。

对于b的Demo如下:

 

对于(? = p)的Demo如下:

 

对于(? ! p)的Demo如下:

具体应用见 下一页

更多详情

JavaScript 基础工具清单

在训练营中,为扩展学员们的编程能力,我们给他们介绍了一些工具和库。目前有位JavaScript学员Kalina,他汇总了这些工具的清单,以分享给其他的代码爱好者。

基于Kalina的清单,我们JavaScript训练营的辅导员Ivan Storck画了一张思维导图:

 

通用

 

脚手架工具(用于启动项目)

  • Yeoman – Yeoman是一个健壮且固定的客户端程序库,包含了工具和框架,有助于开发者快速构建漂亮的web应用。

 

构建工具(自动化构建)

  • Grunt.js – Grunt生态系统很巨大,且每天成长着。由于有数以百计的插件可供选择,你可以使用Grunt自动化构建,且花费最少的代价。
  • Pint.js(Grunt助手) – Pint是一个小型、异步、感知依赖的基于Grunt的封装库,试图解决大规模构建过程中的一些问题。
  • Gulp.js – Gulp的流式构建方式和代码优于配置理念使构建更简单、更直观。
  • Browserify.js(浏览器端) – Browserify是一个允许编写、编译node.js风格的模块并将之用于浏览器的开发工具。就像node,我们在单独文件中编写模块,使用module.exports、exports导出外部方法和属性。
  • Uglify.js – Uglify.js是服务于NodeJS的一个JavaScript解析/压缩/美化库。

 

包管理工具

  • Homebrew(适用于Mac 系统) – Homebrew能安装你需要而苹果不提供的东西。
  • Apt (适用于Ubuntu系统) – apt-get命令是一个强大的命令行工具,与Ubuntu的高级包装工具(APT)配合使用,提供了安装新软件、升级已有软件、更新软件列表目录,甚至升级整个Ubuntu系统等功能。
  • NPM – npm是一个Node.js官方包管理工具。
  • Bower – Bower是一个web的包管理工具。

 

前端

 

MVC框架

  • Backbone.js – Backbone.js通过提供进行键值绑定的模块、自定义事件、具有丰富可枚举函数API的集合、能处理已定义事件的视图,搭建了web应用的架构。它通过RESTful JSON接口与其它已存在的API进行通信。
  • Ember.js – 当底层模块改变时,Ember使得Handlebar模板引擎在保持HTML最新方面做得更好。开始时,你甚至不需要编写任何JavaScript。
  • Angular.js – Angular.js能让你扩展应用的HTML词汇。由此产生的效果是网页极富表现力,代码可读性强,适合快速开发。

 

模板

  • Handlebars.js – Handlebars提供了让你顺利、有效地构建语义模板的强大能力。Handlebars兼容Mustache模板,因此你可以在Handlebars中导入Mustache模板,同时享用Handlebars的其他功能。
  • Mustache.js(比Handlebars更少外置元素) – Mustache是一个简单的web模板系统,已有ActionScript、 C++、Clojure、CoffeeScript、ColdFusion、D、Erlang、Fantom、 Go、Java、JavaScript、 Lua、.NET、 Objective-C、Pharo、Perl、PHP、Python、Ruby、Scala 和 XQuery语言实现版本的可用。
  • Jade – Jade 是一个node模板引擎,主要为node.js的服务器端模板而设计。
  • Haml-js – Haml-js允许在JavaScript项目中使用Haml语法,并拥有大部分与原先Haml相同的功能。
  • Eco – Eco能让你在标记元素中嵌入CoffeeScript 的逻辑。

 

测试

  • Casper.js – CasperJS是一个Javascript实现的,PhantomJS和SlimerJS的导航脚本和测试工具。
  • Zombie.js – Zombie.js是一个在模拟环境中测试客户端JS代码的轻量级框架。无需浏览器。

 

后端

 

服务器

  • Express  – Express是一个Node的web应用框架。
  • Node – Node.js是一个基于Chrome JavaScript 运行时建立的平台, 用来方便地搭建快速、 易于扩展的网络应用。

 

数据库

  • MongoDB – MongoDB 是个开源的文档数据库,引领着NoSQL数据库。
  • Postgresql – PostgreSQL 是一个强大、开源、对象-关系型数据库系统。
  • SQL – SQL用于与数据库进行通信。根据美国国家标准学会的定义,它是关系式数据库管理系统的标准语言。

 

架构风格

  • RESTful – 表现层状态转化是一种架构风格,包含了一组相互协作的架构约束。这些约束应用于分布式超媒体系统之间的组件、连接器和数据元素。

 

测试

  • Cucumber.js – Cucumber.js是一种流行的行为驱动开发工具,并将之应用于你自己的JavaScript程序
  • Jasmine – Jasmine是JavaScript的一个行为驱动开发测试框架。它不依赖于浏览器、DOM、或任何其他JavaScript框架。因此它适用于网站、Node.js项目、或任何可运行JavaScript的地方。
  • Mocha – Mocha 是一个运行于node.js和浏览器的特色丰富的JavaScript测试框架,使得异步测试更简单有趣。
  • Q-Unit – Q-Unit 是一个强大,易用的JavaScript单元测试框架。它被用于jQuery、jQuery UI 和 jQuery Mobile 项目,能够测试任何通用的JavaScript代码。

 

断言库

  • Chai – Chai 是一个行为驱动开发/测试驱动开发(BDD / TDD )断言库,用于node和浏览器,可以愉快地搭配任何JavaScript测试框架。

 

函数式编程工具

  • Underscore.js – Underscore是一个提供了一大堆有用的函数式编程辅助,无须扩展任何内置对象的JavaScript库。
  • Lo-Dash – Lo-Dash是一个提供了一致性、定制和性能的实用程序库。

JavaScript高级程序设计(第3版)高清完整PDF中文+英文+源码  下载见 http://www.linuxidc.com/Linux/2014-09/107426.htm

如何使用JavaScript书写递归函数  http://www.linuxidc.com/Linux/2015-01/112000.htm

JavaScript核心概念及实践 高清PDF扫描版 (邱俊涛)  http://www.linuxidc.com/Linux/2014-10/108083.htm

理解JavaScript中的事件流  http://www.linuxidc.com/Linux/2014-10/108104.htm 

JavaScript跨浏览器事件对象类库 http://www.linuxidc.com/Linux/2015-07/120615.htm

产品经理经常去的10大产品网站

1,淘宝UED http://ued.taobao.com/blog
淘 宝网用户体验团队博客,有关用户体验设计和研究的经验分享。UED的本意是用户体验设计,是英文User Experience Design的缩写。通常的理解,他们做的一切都是为了呈现在您眼前的页面。他们关心用户的操作,关心用户的感受。为了用户更好的购物体验,为了用户每月 达成更多的交易,为了用户的满意

2,新浪UED http://ued.sina.com
一个关注用户体验、关注工作流、关注作品质量的有爱团队。关注交互设计、前端开发、团队活动、用户研究、网页重构、视觉设计等与用户体验和界面设计有关的研究。

3,阿里巴巴UED http://www.aliued.cn/
阿里巴巴(中国站)用户体验设计部博客,讨论研究关于交互设计、视觉设计、前端开发、用户研究等方面

4,UCD大社区 http://ucdchina.com/
“UCD 大社区”是用户体验和产品设计行业的综合社区,他们从一个粗糙的群体博客开始,慢慢走向开放、更开放! 一切内容均经过审核,主题围绕“以用户为中心的设 计”。为方便快速访问其他行业网站和资源,他们编辑了一些国内外优秀的设计网址,合成“网址导航”(http://ucdchina.com/123)。 此外,他们还在为一些企业提供“以用户为中心的产品设计培训”,及“互联网产品设计咨询和顾问”等商业服务。

5,支付宝UED
交互设计:http://ped.alipay.com/ PED(Product Experience Design)产品设计团队,专注于产品设计、交互设计、体验设计领域的不断实践和创新。
视觉设计:http://upd.alipay.com/ UPD 讨论视觉设计领域(1)字体设计(2)标志设计 (3)插图设计 (4)编排设计 (5)广告设计(6)包装设计(7)展示设计
用 户研究:http://ued.alipay.com  UED 用户研究的目的是帮助企业定义产品的目标用户群、明确、细化产品概念,并通过对用户的任务操作特性、知觉特征、认知心理特征的研究,使用户的实际需求成为 产品设计的导向,使您的产品更符合用户的习惯、经验和期待

6,百度MUX http://mux.baidu.com/
百 度无线MUX(Baidu Mobile User Experience Department),坚持以用户为中心的设计,以提升产品的体验为终极使命,追求“简单极致”的设计理念,负责着所有无线产品的视觉,交互,用户研究 方面的工作,并致力于做行业内最优秀,体验最好的无线产品

7,腾讯CDC http://cdc.tencent.com/
腾 讯CDC(Customer Research & User Experience Design Center用户研究与体验设计中心)作为腾讯的核心部门之一,向着“做世界一流的互联网设计团队,为用户创造优质‘在线生活’体验”这一愿景努力,致力 于不断提升腾讯全线产品的用户体验

8,携程UED http://ued.ctrip.com/blog/
做人性的网站,让在线预订过程成为一种享受,一种愉悦的经验;讨论界面设计和前端开发等话题

9,百度泛用户体验 http://www.baiduux.com/
关于泛用户体验的360度全方位讨论和分享——无论是视觉设计、交互体验、还是前端开发、用户研究。是以‘用户体验’为核心的跨专业分享平台,以用户为核心,将提升产品体验做为终极目标,完成百度WEB产品的视觉、交互设计、前端开发,用户研究、内容优化等工作

10,腾讯WSD http://wsd.tencent.com/
腾讯WSD是腾讯无线业务系统一个致力于提升移动设备上用户体验的专业设计团队。工作领域覆盖移动设备上的网站、软件、游戏等产品的用户研究、交互设计、视觉设计和网页重构。

11,微博UDC http://udc.weibo.com/
微博用户研究与体验设计中心(User Research & Experience Design Center) ,致力于SNS用户体验设计,为微博产品提供专业解决方案

12,网易UEDC http://uedc.163.com/
网易用户体验设计中心(User Experience Design Center),设计中心服务的产品包括网易门户、邮箱、博客、无线、交友、基础产品等,有关于交互设计、用户研究、视觉设计等方面讨论

13,CUED-迅雷用户体验设计中心 http://cued.xunlei.com/
迅雷用户体验设计中心,关注交互设计、网页重构、视觉设计

看 到这些著名互联网公司都有自己的用户体验部门并且建设了自己的博客,这不但总结分享了产品设计的经验,而且也是对公司和产品的形象的宣传,更是促进了行业 良性发展。他们不仅仅可以给互联网行业学习参考,同样也适用于企业应用软件开发,甚至,企业应用开发者更加要学习互联网的产品设计经验,因为企业应用其实 已经落后于互联网行业产品了。

function a(){}与var a=function(){}

据说是与js预编译有关
function a()
{
}
是定义一个函数.在预编译期声明了一个对象,指向一个函数

var a=function()
{
}
在预编译期只是声明了一个变量, 再运行期才会把函数指给a.

这个区别在使用闭包时,需要注意.
譬如如下代码
var Util = Util || (
function()
{
return {
count:function()
{
innerCount();//innerCount is not a function
}
};
//is wrong, undefined,未初始化,只有预编译阶段,没有运行。上边调用的时候,innerCount和函数还没有绑定。
var innerCount = function()
{
}
//is ok
function innerCount()
{
}
}
)();

var Util = Util || (
function()
{
//is ok, 运行过了.定义后再使用是ok的。
var innerCount = function()
{
}

return {
count:function()
{
innerCount();
}
};
//is ok
function innerCount()
{
}
}
)();

Javascript学习小结

作者QQ:415074476

QQ群:191280586

note:解读的Javascript权威指南。
1.变量作用域是全局性的。而不是从定义处开始
例:
var g = “global”;
function a()
{
alert(g);//output undefined, not global
var g=”local”;
alert(g);//output local
}

2.判断一个变量是否有值。用if(typeof(x) ==”undefined”),而不是if(x).因为如果x未声明,会报js错误。另外,”undefined”和undefined不同,前者类型为string,后者类型为undefined.
另外:
不能用if(a===undefined)这样会报错,
但是可以用if(a.b===undefined)

3.基本类型:数值,布尔,null, undefined. 引用类型:对象,数组,函数。 使用引用类型拷贝赋值时得注意。会影响原来变量的值。
如:
var a=[1,2];
var b=a;
b[2]=”3″;
alert(a);//1,2,3
alert(b);//1,2,3
function test(d)
{
d[3]=”haha”;
}
test(a);
alert(a);//1,2,3,haha
alert(b);//1,2,3,haha

4.代码验证ret+=str的效率还是比ret.push(str);return ret.join(“”);高

5.闭包,使用需慎重,需要了解执行环境
应用场景
1、保护函数内的变量安全。
2、在内存中维持一个变量。

6.delete不能删除由var声明及某些内部的核心属性和客户端属性

7.void 主要用在超链中。如open new window

8.关于.和[]操作符。 .的右边只能是直接量的标识符,不能是字符串或变量。[]没有这限制

9.()操作符用于调用函数。第一个运算数总是一个函数名或者是一个引用函数的表达式。主要是得习惯
(function(){alert(1);})() 这种代码。可以把第一个运算数理解成匿名函数。
等同于。
function anonymous()
{
alert(1);
}
anonymous();
用于加载即执行的功能,并且只执行一次。可以和闭包一起用。

10.异常处理。
a.抛出异常 throw new Error(“test error”);
b.捕获异常try{}catch(){} finally{}

11.with语句
用于暂时修改作用域链,可以减少代码。。不建议使用。

12.通用的Object属性和方法
属性:constructor 等于构造函数
方法:toString(),用于字符串环境
toLocaleString()
valueOf().用于数值环境
hasOwnProperty(x)是否有一个非继承的属性x
propertyIsEnumerable(x).判断一个非继承的属性是否可用于for in语句
isPrototypeOf()

13.数组
4种方法
var a=[];//数组直接量
var a= new Array();//空数组
var a=new Array(“a”,”b”);//初始化
var a=new Array(10);//指定长度

数组的length属性可读写。当设定length时,会增加/截断数组
数组的length只影响数字索引,或被数字索引影响。

数组方法:
join() ;//转成字符串
reverse();//倒序
sort();//排序
concat();//连接数组
slice();//取数组的一部分
splice();//插入或删除数组。
push(),pop();//尾部增删
unshift(),shift();//头部增删

14.执行匿名函数的3种方法
(function (){alert(1);}());//比较正常
(function(){alert(2);})();
void function (){alert(3);}();

15.&& ,|| 的返回值问题。。这点得注意和其它语言不同。其它语言是返回布尔值。
javascript是返回执行的表达式的值。
如 var s = “a” || “b”;//”a”
var s = false || “good” ;//”good”
var s = “d” && “b”;//”b”
var s = false || “d”;//”d”

16.函数相关。
三种定义的方式:
a,语句。function a(){} //typeof a ==”function”
b,表达式,直接量 var a=function(){}//typeof a ==”function”
c,构造函数 var a = new Function(“x”,”y”,”return x+y””); //typeof a ==”object”,不使用词法作用域,而是当作顶层函数编译。

函数属性:
a,length 声明函数的形参个数,函数内和函数外都可访问,只读属性
b,prototype
c,定义函数自己的属性,相当于静态变量。
如uniquetest.counter=0; function uniquetest(){
return uniquetest.counter++;
}

函数方法
a,call()和apply()
用于改变this对象引用的对象。call是按个传参。apply是传一个数组
使用apply的好像是可以直接利用arguments对象

17.关于原型对象。prototype
每个对象都包含着对它的原型对象的内部引用。原型对象的任何属性表现为每个以它为原型对象的属性。
换句话说:javascript对象从它的原型那里继承属性。

//code
function a(x,y)
{
this.x = x;
this.y = y;
}

var d = new a(2,3);
alert(a.prototype);
alert(d.constructor);//constructor继承自a.prototype.constructor
alert(d.constructor.prototype);//
alert(d.constructor.prototype.constructor);//

说明:定义a时。a自动地有prototype属性,prototype的初始值为一个对象。这个对象只有constructor属性,指向a本身。
创建d对象时,d继承a的prototype的属性,所以d有属性constructor,指向构造函数a。

区分是自有属性还是通过prototype继承的属性,只能通过api Object.hasOwnProperty()

18.类属性。
由于构造函数本身也是一个对象。对象可以有自己的属性。这个属性也就成了类的属性。
//code1
a.count =0;
function a(x,y)
{
this.x=x;
this.y=y;
a.count++;
this.counter = function()
{
return a.count;
}
}
var b = new a(12,3);//a.count=1
var c = new a(432,23);//a.count=2
//这样可以用count保存调用构造函数的次数。但是有一个弊端。a.count是可以随便改变的,即a.count属性是可写的。
a.count=100;
var d = new a(3,3);//这个时候a.count=101而不是期待的3
//有什么办法来保护count呢?闭包的用武之地来了。

//code2
var a = (function()
{
var count=0;
return function(x,y)
{
this.x=x;
this.y=y;
count++;
this.counter = function()
{
return count;
}
}
})();

19。 || 的使用
为了避免文件的重复包含可能导致的错误。
使用var varibale = varibale || {}的用法,是比较好的。
尤其是在执行代码中有初始化,又只能初始化一次的情况下。

如loadflash.js
var a = function(){var init=0;//假设这里是动态加载flash
return {}}();
如果包两次loadflash.js,就会加载两次flash,导致取flash对象时出现异常。
如果换成var a = a || function(){var init=0;//假设这里是动态加载flash
return {}}();
不管两几次loadflash.js文件,也不会出问题了

20通用对象模型
a.toString()在字符串环境中使用
b.valueOf()在数值环境中使用。在某些字符串环境中valueOf()方法优先级高于toString()
c.=号是按地址比较,要比较两个对象的值,需要定义equals方法。

21。子类
得指定prototype

22正则表达式
几个少用的。
(?=p)正前向声明 要求接下来的字符与模式p匹配
(?!p)反前向声明 要求接下来的字符不与模式p匹配

二.关于兼容.
一、函数和方法差异
1. getYear()方法
【分析说明】先看一下 以下代码:
var year = new Date().getYear();
document.write(year);
在IE中得到的日期是”2010″,在Firefox中看到的日期是”110″,主要是因为在 Firefox 里面 getYear 返回的是 “当前年份-1900” 的值。
【兼容处理】
加上对年份的判断,如:
var year = new Date().getYear();
year = (year < 1900 ? ( 1900 + year):year);
document.write(year);
也可以通过 getFullYear getUTCFullYear 去调用:
var year = new Date().getFullYear();
document.write(year);

2. eval()函数
【分析说明】在IE中,可以使用eval(“idName”)或 getElementById(“idName”)来取得id为idName的 HTML对象;Firefox下只能使用getElementById(“idName”)来取得id为idName的HTML对象。
【兼 容处理】统一用getElementById(“idName”)来取得id为idName的HTML对象。

3. const声明
【分析说明】在 IE 中不能使用 const 关键字。如:
const constVar = 32 ;
在IE中这是语法错误。
【兼 容处理】不使用 const ,以 var 代替。

4.var
【分析说明】请看 以下代码:
echo = function (str){
document.write(str);
}
这个函数在IE上运行 正常,Firefox下却报错了。
【兼容处理】而在echo前加上var就正常了,这个就是我们提到var的目的。

二、样式访问和设置

1 . CSS的”float”属性

【分析说明】Javascript访问一个给定CSS 值的最基本句法是:object.style.property,但部分CSS属性跟Javascript中的保留字命名相同, 如”float”,”for”,”class”等,不同浏览器写法不同。

在IE中这样写:
document.getElementById( ” header ” ).style.styleFloat = ” left ” ;
在Firefox中这样写:
document.getElementById( ” header ” ).style.cssFloat = ” left ” ;
【兼容处理】在写之前加一个判断,判断浏览器是否是IE:
if (document.all){
document.getElementById( ” header ” ).style.styleFloat = ” left ” ;
}
else {
document.getElementById( ” header ” ).style.cssFloat = ” left ” ;
}
2 . 访问

3. 访问和设置class属性

【分析说明】同样由于class是Javascript保留字的原因,这两种浏览器使用不同的 JavaScript 方法来获取这个属性。
IE8.0之前的所有IE版本的写法:
var myObject = document.getElementById( ” header ” );
var myAttribute = myObject.getAttribute( ” className ” );
适用于IE8.0 以及 firefox的写法:
var myObject = document.getElementById( ” header ” );
var myAttribute = myObject.getAttribute( ” class ” );
另外,在使用setAttribute()设置Class属性的时候,两种浏览器也存在同样的差异。
setAttribute(“className”,value);
这种写法适用于IE8.0之前的所有IE版本,注意:IE8.0 也不支持”className”属性了。
setAttribute(“class”,value);适用于IE8.0 以及 firefox。
【兼容处理】
方法一,两种都写上:
var myObject = document.getElementById( ” header ” );
myObject.setAttribute( ” class ” , ” classValue ” );
myObject.setAttribute( ” className ” , ” classValue ” );
// 设置header的class为 classValue
方法二,IE和FF都支持 object.className,所以可以这样写:
var myObject = document.getElementById( ” header ” );
myObject.className = ” classValue ” ; // 设置header的class为classValue
方法三,先判断浏览器类型,再根据浏览 器类型采用对应的写法。

4. 对象宽高赋值问题
【分析说明】FireFox中类似 obj.style.height = imgObj.height 的语句无效。
【兼容处理】统一使用 obj.style.height = imgObj.height + ‘px’;

三、 DOM方法及对象引用
1 . getElementById
【分析说明】先来看一组代 码:

< input id =”id” type =”button”
value =”click me” onclick =”alert(id.value)”/ >
在 Firefox中,按钮没反应,在IE中,就可以,因为对于IE来说,一个HTML 元素的 ID 可以直接在脚本中当作变量名来使用,而Firefox中不可以。

【兼容处理】尽量采用W3C DOM 的写法,访问对象的时候,用 document.getElementById(“id”) 以ID来访问对 象,且一个ID在页面中必须是唯一的,同样在以标签名来访问对象的时候,用document.getElementsByTagName(“div”) [0] 。该方式得到较多浏览器的支持。


< input id =”id” type =”button” value =”click me”
onclick =”alert(document.getElementById(‘id’).value)” / >

2. 集合类对象访问
【分析说明】IE下,可以使用()或[]获取集合类对象;Firefox下,只能使用 []获取集合类对象。如:
document.write(document.forms( ” formName ” ).src);
//该写法在IE下能访问到Form对象的src属性
【兼容处理】将document.forms(“formName”)改为 document.forms[“formName”]。统一使用[]获取集合类对象。

3 . frame的引用
【分析说明】IE可以通过id或者name访问这个frame对应的window对象,而Firefox只可以通过 name来访问这个frame对 应的window对象。
例如如果上述frame标签写在最上层的window里面的htm里面,那 么可以这样访问:
IE: window.top.frameId或者window.top.frameName来访问这个window对象;
Firefox:只能 这样window.top.frameName来访问这个window对象。
【兼容处理】使用frame的name来访问frame对 象,另外,在IE和Firefox中都可以使用 window.document.getElementById(”frameId”)来访问这个frame对象。

4 . parentElement
【分析说明】IE中支持使用parentElement和parentNode获取父节点。而 Firefox只可以使用parentNode。
【兼容处理】因为firefox与IE都支持DOM,因此统一使用 parentNode来访问父节点。

5 . table操作
【分析说明】IE下 table中无论是用innerHTML还是appendChild插入
都没有效果,而其他浏览器却显示正 常。 【兼 容处理】解决的方法是,将加到table的元素中,如下面所示: var row = document.createElement( ” tr ” ); var cell = document.createElement( ” td ” ); var cell_text = document.createTextNode( ” 插 入的内容 ” ); cell.appendChild(cell_text); row.appendChild(cell); document.getElementsByTagName( ” tbody ” )[ 0 ].appendChild(row);

6 . 移除节点removeNode()和removeChild()
【分析说明】appendNode在IE和Firefox下都能正常使用,但是removeNode只能在IE下用。
removeNode方法的功能是删除一个节点,语法为node.removeNode(false)或者 node.removeNode(true),返回值是被删除的节点。
removeNode(false)表示仅仅删除指定节点,然 后这个节点的原孩子节点提升为原双亲节点的孩子节点。
removeNode(true)表示删除指定节点及其所有下属节点。被删除的 节点成为了孤立节点,不再具有有孩子节点和双亲节点。
【兼容处理】Firefox中节点没有removeNode方法,只能用 removeChild方法代替,先回到父节点,在从父节点上移除要移除的 节点。
node.parentNode.removeChild(node);
// 为了在ie和firefox下都能正常使用,取上一层的父结点,然后remove。

7 . childNodes获取的节点
【分析说明】childNodes的下标的含义在IE和Firefox 中不同,看一下下面的代码:
< ul id =”main” >
< li > 1
< li > 2
< li > 3

< input type =button value =”click me!” onclick =
“alert(document.getElementById(‘main’).childNodes.length)” >
分别用IE和 Firefox运行,IE的结果是3,而Firefox则是7。Firefox使用DOM规范,”#text”表示文本(实际是无意义 的空格和换行等)在Firefox里也会被解析成一个节点,在IE里只有有实际意义的文本才会解析成”#text”。
【兼容处理】
方法一,获取子节点时,可以通过node.getElementsByTagName()来回避这个问题。但是 getElementsByTagName对复杂的DOM结构遍历明显不如用childNodes,因为childNodes能更好的处理DOM的层次结 构。
方法二,在实际运用中,Firefox在遍历子节点时,不妨在for循环里加上:
if (childNode.nodeName ==”#text” ) continue ; // 或者使用nodeType == 1。
这 样可以跳过一些文本节点。

8. Firefox不能对innerText支持
【分析说明】 Firefox不支持innerText,它支持textContent来实现innerText,不过textContent没有像 innerText一样考虑元素的display方式,所以不完全与IE兼容。如果不用textContent,字符串里面不包含HTML代码也可以用 innerHTML代替。也可以用js写个方法实现,可参考《为firefox实现innerText属性 》一文。

【兼容处理】通过判断浏览器类型来兼容:
if (document.all){
document.getElementById( ‘element’ ).innerText = ” my text ” ;
} else {
document.getElementById( ‘element’ ).textContent = ” my text ” ;
}

四、事件处理
如果在使用 javascript的时候涉及到event处理,就需要知道event在不同的浏览器中的差异,主要的JavaScript的事件 模型有三种(参考《Supporting Three Event Models at Once 》),它们分别是NN4、IE4+和W3C/Safar。

1 . window.event
【分析说明】先看一段代码
function et()
{
alert(event); // IE: [object]
}
以上代码在IE运行的结果是 [object],而在Firefox无法运行。

因为在IE中event作为window对象的一个属性可以直接使用,但是在 Firefox中却使用了W3C的模型,它是通过传参的方法来传播 事件的,也就是说你需要为你的函数提供一个事件响应的接口。
【兼容处 理】添加对event判断,根据浏览器的不同来得到正确的event:
function et()
{
evt = evt ? evt:(window.event ? window.event: null );
// 兼容IE和Firefox
alert(evt);
}

2 . 键盘值的取得
【分 析说明】IE和Firefox获取键盘值的方法不同,可以理解,Firefox下的event.which与IE下的 event.keyCode相当。关于彼此不同,可参考《键盘事件中keyCode、which和charCode 的兼容性测试 》
【兼容处理】

function myKeyPress(evt){
//兼容IE和Firefox获得keyBoardEvent对象
evt = (evt) ? evt : ((window.event) ? window.event : “” )
// 兼容IE和 Firefox获得keyBoardEvent对象的键值
var key = evt.keyCode ? evt.keyCode:evt.which;
if (evt.ctrlKey && (key == 13 || key == 10 )){
// 同时按下了Ctrl和回车键
// do something;
}
}

3 . 事件源的获取
【分析说明】在使用事件委托的时候,通过 事件源获取来判断事件到底来自哪个元素,但是,在IE下,event对象有srcElement属性,但是 没有target属性;Firefox下,even对象有target属性,但是没有srcElement属性。

【兼容处理】

ele = function (evt){ // 捕获当前事件作用的对象
evt = evt || window.event;
return
(obj=event.srcElement?event.srcElement:event.target; );
}

4 . 事件监听

【分析说明】在事件监听处理方面,IE提供了attachEvent和detachEvent两个接口,而Firefox提供 的是 addEventListener和removeEventListener。
【兼容处理】最简单的兼容性处理就是封装这两套接 口:

function addEvent(elem, eventName, handler) {
if (elem.attachEvent) {
elem.attachEvent( ” on ” + eventName, function (){
handler.call(elem)});
//此处 使用回调函数 call(),让this指 向elem
} else if (elem.addEventListener) {
elem.addEventListener(eventName, handler, false );
}
}
function removeEvent(elem, eventName, handler) {
if (elem.detachEvent) {
elem.detachEvent( ” on ” + eventName, function (){
handler.call(elem)});
//此处 使用回 调函数call(),让this指向elem
} else if (elem.removeEventListener) {
elem.removeEventListener(eventName, handler, false );
}
}
需要特别注意,Firefox下,事件处理函数中的this指向被监听元素本身,而在IE下则不然,可使用回调函数call,让当前上下文指向监听的元素。

5 . 鼠标位置
【分析说明】IE下,even对象有x,y属性,但是没有pageX,pageY属性;Firefox下,even对象有 pageX,pageY属 性,但是没有x,y属性。
【兼容处理】使用mX(mX = event.x ? event.x : event.pageX;)来代替IE下的event.x或者Firefox下的event.pageX。复杂点还要考虑绝对位置。

function getAbsPoint(e){
var x = e.offsetLeft, y = e.offsetTop;
while (e = e.offsetParent) {
x += e.offsetLeft;
y += e.offsetTop;
}
alert( ” x: ” + x + ” , ” + ” y: ” + y);
}

五、 其他差异的兼容处理
1 . XMLHttpRequest
【分析说明】new ActiveXObject(“Microsoft.XMLHTTP”);只在IE中起作用,Firefox不支持,但支持XMLHttpRequest。
【兼容处理】

function createXHR() {
var xhr = null ;
if (window.XMLHttpRequest){
xhr = new ActiveXObject( ” Msxml2.XMLHTTP ” );
} else {
try {
xhr = new ActiveXObject( ” Microsoft.XMLHTTP ” );
}
catch () {
xhr = null ;
}
}
if ( ! xhr) return ;
return xhr;
}

2 . 模态和非模态窗口
【分析说明】IE中可以通过showModalDialog和showModelessDialog打开模态和非模态窗 口,但是Firefox不支 持。
【解决办法】直接使用window.open(pageURL,name,parameters)方 式打开新窗口。 如果需要传递参数,可以使用 frame或者iframe。

3. input.type 属性问题
IE下 input.type属性为只读,但是Firefox下可以修改

4 . 对select元素的option操作
设置options,IE和Firefox写法不同:
Firefox:可直接设 置
option.text = ‘ foooooooo ‘ ;
IE:只能设置
option.innerHTML = ‘ fooooooo ‘ ;

删除一个select的option的方法:
Firefox:可以
select.options.remove(selectedIndex);
IE7:可以用
select.options[i] = null ;
IE6:需要写
select.options[i].outerHTML = null ;

5 . img对象alt和title的解析
【分析说明】img对象有alt 和title两个属性,区别在于,alt:当照片不存在或者load错误时的提示。
title:照片的tip说明, 在IE中如果没有定 义title,alt也可以作为img的tip使用,但是在Firefox中,两者完全按照标 准中的定义使用
在定义img对象时。
【兼容处理】最好将alt和title对象都写全,保 证在各种浏览器中都能正常使用 。

6 . img的src刷新问题
【分析说明】先看 一下代码:
< img id =”pic” onclick = “this.src= ‘a.jpg'”
src =”aa.jpg” style =”cursor: pointer” />
在IE 下,这段代码可以用来刷新图片,但在FireFox下不行。主要是缓存问题。
【兼容处理】在地址后面加个随机数就解决了:
< img id =”pic” onclick = “javascript:this.src=this.src+’?’
+Math.random()”src =”a.jpg” style =”cursor: pointer” />