Skip to content

IE下载保存base64文件

如果一个超链接的链接地址是一个base64的代码,如下:

<a href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABJdJREFUeNrsV2toXEUYPXPvPtPsJpuNTdo0zTsGrbam1oBWJFVjJUrTai1YKMZCMaItKFL1R1GhBEpJQXyiECgqiELFEgmijamgBolpkiZtHrt5tzHJJpvd7PPeu+M3txtN2sZsodgfZuDjDnfmO+fM95i7yzjnuJVDwi0eKwJWBKwIYInsefXzzDxrinaYM7WMydrGGNdgNaZAkhwIRtwEYrqkRU11tTsmT9D+2M0SIL15KnOfwRr5yGgwm0uzDiDX+TiKnVkwSNmLNg75L6F7/Bj+GP0UAV/Sa7VVk3WJCrmugGffcqbmb2E/25PSNjxc/A5K1+7BrAqMhgDvbCXG0ICoqsEsc3CCKLTJKEgGTFIAjb3PoNX12zdHn5jeTVDqjQpgL32yOj81O9JflvMcthUdx0TUgLapGFwBDTIzwhFmmLXS9c0jtF3TnWIEo3EJmxxmPHAb8PvYC2ju/Pr4kUrP639vSkAAq/kwoyAtL9T31F11uGftfoz6gZkwINOiTOUq0e6fBhjK8zliCwLM46bRO6sJyE8Bvui6G3s3dFJcEPg3AYYFc5OjSOnbWXIChZnP48wUdBJBKhG5zK6IUOndBM1j10keJ6UaBT04B+Taq/D2D0NzikqKNLlJU/nJ2h0TJ6+ujXkYY7sDw+5zb2RWrj+My3ONYFzVe1SJBdEz2YALE6dhkJNIkAUxCj/XU0AATCaQKzCiOxilqdBZgdKsGr0EGDMgqgXQOvotmi/Wtx3aGh7OSTc/PV8fwlNuuy/9mGMu8IqlqxVT3u8wNtsABT590cgcWJe8HWElhGbXexQFI5Fr2F78Ps7/+RnGfC1EIkF81rVYFA8VvAyL0YrRue+hcK8uzkTtmpX8CKYD48i011BB546XOuX14nyCY5Wvotjje7DI3FOzBh29DejpiyISvXIqC0WwpMiCjdnb4PH7MOhpJUJGhDH9KcgFSYzyleMshdNmR/vIGVzsCyMcxQIME+7IKsPtGUdgNG9Ciydwuio9eZeoAcvgFot58EAaensb0d0hQep6DJLXqTsrqR50hM4ipjXhznXlUDVGNSFIxanng8j1ArRZ7WgbbML5LnrbVbEIoz3UDEX7BY6UX7H50Z0odQ0+SUtWIUD+qsKLkpkLGHBpRF5ORXgvCrbm6M6u/iH0E2B/UhMy07qJSJz+mu6l/DN4gt3oH6C8L4HhTvoRGekf49RRBcxAGbh/2CzqjKsefm7WP4OoCJk3HQWFOVDUiG5iDjpJ1L8K/sg0uAg/8V9rDN4QYShLYyh+G4JhP6yaEdExflYoFxGI1O4a2f9uS06rzcbhYxRLxv/pDzGXOGyr56iazRjqkfS2XHSDcNGyDCmbVdjsy2CoZrg6ma/+4MghWgkLAUGyywfLhsro6di3N/KB2+3Ozy/I033dLjc5Rdx1u8dfTORqFXfLMhgiRj7BSRaa12gS3UBmJ1tTXV39pcEgkkSMqjpcX1+/Z94hAQHWBDCi8RtSubqajGSidLPiYhBXO0bmiatfbtwQBlsCICkeFcTVBhMkv5kY/9EvopV/RisC/vcC/hJgABG9CR0EnLuyAAAAAElFTkSuQmCC" download="picture-test.png">下载</a>

页面中的效果显示如下:

下载

在Chrome等现代浏览器当中,点击下载会直接保存图片。但是在IE当中无效,因为IE不支持直接保存base64文件。那IE如何解决这个问题呢?

在IE当中可以使用 msToBlob() 方法转换成 blob 文件,然后用 msSaveBlob() 方法来保存。


var blobObj = new Blob( [ $('a').attr('href') ] );
// 或者直接将 canvas 文件转成 blob,var blobObj = canvas.msToBlob();
navigator.msSaveBlob(blobObj, 'picture-test.png');

官方参考:https://technet.microsoft.com/ZH-CN/Library/hh779016.aspx

HTML5文件保存相关库:https://github.com/eligrey/FileSaver.js

重装系统后环境搭建和软件安装

本文只是个人配置的备忘录,并不具备权威参考价值。

[w]:Windows 系统
[m]:Mac 系统


[w] 0 安装 Windows 10 企业版(英文版):
en_windows_10_enterprise_version_1703_updated_july_2017_x64_dvd_10925376.iso
ed2k://|file|en_windows_10_enterprise_version_1703_updated_july_2017_x64_dvd_10925376.iso|4794277888|C66909F9185A783AD6E856CB1BC5771B|/

[w] 1 破解系统
链接:http://pan.baidu.com/s/1nv2wO33
密码:marw

[w] 2 系统设置
2.0 登陆Microsoft帐户
2.1 更改用户控制UAC Win+R -> msconfig -> Tools
2.2 更改桌面和任务栏为小图标
2.3 格式化磁盘、设置盘符
2.4 更改计算机名,加域。修改盘符(如果需要)
2.5 Settings -> Language -> 下载中文Handwriting、Speech
2.6 Control Panel – Clock, Language, and Region – Change date, time, or number formats,三个选项卡都选择 Chinese/China
2.7 打开休眠 cmd -> powercfg /a -> powercfg -h on
Control Panel\Hardware and Sound\Power Options\System Settings

3 安装软件

2.1 好用的VPN工具(定期更新)
2.2 Chrome 登陆同步书签,默认搜索百度
2.3 百度网盘
2.4 Clover
2.5 Everything
2.6 TeraCopy
2.7 有道云笔记
2.8 7-Zip
2.9 GitHub
2.10 Sublime Text
2.11 QQ输入法 设置皮肤
2.12 网易云音乐
2.13 QQ || TIM
2.14 腾讯电脑管家
2.15 [w] Office 2013 KMSpico 密码:sdos
   [m] 可靠有效的Office for Mac 15.19.1破解版
2.16 Photoshop
2.17 Git SourceTree
2.18 TortoiseSVN
2.19 微信
2.20 JRE -> Tomcat
2.21 FileZilla
2.22 NVM -> Node.js
2.23 QQ影像
2.24 有道词典
2.25 SecureCRT 密码:0w9b
2.26 [w][m] JDK & Eclipse for Java EE
2.27 [w] 启用IIS & PHP & Mysql & Navicat for MySQL 密码:oh22 & IIS支持PHP
2.28 迅雷
2.29 [w] Tickeys
2.30 Adobe Animate
2.31 [w] 迅雷影音
   [m] KMPlayer
2.32 Firefox
2.33 nexusfont
2.34 caesium
2.35 iTunes
2.36 [w] 格式工厂
2.37 [w] pngGauntlet 密码:pqgk
2.37 [w] Composer
2.38 [w] Compare Advance
2.39 [w] scoop
2.40 Python

4 设置邮件客户端

网页使用html5录音

实现原理

通过 HTML5 的 getUserMedia 接口,可以让页面在浏览器当中访问客户端的硬件设备(摄像头和麦克风)。然后配合 Web Audio API 使用 <audio> 标签来存储录音。
具体解释:https://www.html5rocks.com/zh/tutorials/getusermedia/intro/

具体实现

开源组件:
https://github.com/feizhaojun/mRecorder
https://github.com/mattdiamond/Recorderjs

其他资料
Getting Started with Web Audio API
WEB AUDIO API简易入门教程
MDN->navigator.getUserMedia
MDN->MediaDevices.getUserMedia()
WebRTC 1.0: Real-time Communication Between Browsers

Discuz论坛不能批量上传图片

1.任何网站实现批量上传都需要借助flash或者sliverlight(微软的,几乎歇菜了),Discuz的批量上传是借助flash的。

2.因为flash如此强大,可能获取到用户的客户端信息,所以现代浏览器(尤其是谷歌浏览器)目前都是默认不自动执行flash的。

以上就是原因。解决方案如下:

在地址栏左侧叹号那里点击小叹号,找到 flash,默认允许。不过这个需要用户操作,你作为网站管理员是掌控不了的。

另外,flash马上就要退出历史舞台了,以后批量上传会有新的方式来替换,但是目前没有新的成熟方法,所以很长一段时间内(可能三五年),批量上传都是网站开发者的大问题。

另外的另外,以后的浏览器隐私设置会越来越紧,作为用户更安全了,但是很多操作会不方便了。

人工智能技能图谱

此文是想要进入人工智能这个领域、但不知道从哪里开始的初学者最佳的学习资源列表。原文是 Ray Alez 编写的“Artificial Intelligence resources”,简单翻译后供大家参考。

一、机器学习

有关机器学习领域的最佳介绍,请观看Coursera的Andrew Ng机器学习课程。 它解释了基本概念,并让你很好地理解最重要的算法。

这些不错的资源你可能也感兴趣:

  1. Perer Norvig 的Udacity Course on ML(ML Udacity 课程)

  2. Tom Mitchell 在卡梅隆大学教授的 Another course on ML(另一门ML课程)

  3. YouTube上的机器学习教程 mathematicalmonk

二、深度学习

关于深度学习的最佳介绍,我遇到最好的是 Deep Learning With Python。它不会深入到困难的数学,也没有一个超长列表的先决条件,而是描述了一个简单的方法开始DL,解释如何快速开始构建并学习实践上的一切。它解释了最先进的工具(Keras,TensorFlow),并带你通过几个实际项目,解释如何在所有最好的DL应用程序中实现最先进的结果。

在Google上也有一个great introductory DL course,还有Sephen Welch的great explanation of neural networks

之后,为了更深入地了解,这里还有一些有趣的资源:

  1. Geoffrey Hinton 的coursera 课程“Neural Networks for Machine Learning。这门课程会带你了解 ANN 的经典问题——MNIST 字符识别的过程,并将深入解释一切。

  2. MIT Deep Learning深度学习)一书。

  3. UFLDL tutorial by Stanford (斯坦福的 UFLDL 教程)

  4. deeplearning.net教程  

  5. Michael Nielsen 的 Neural Networks and Deep Learning(神经网络和深度学习)一书

  6. Simon O. Haykin 的Neural Networks and Learning Machines (神经网络和机器学习)一书

三、人工智能

Artificial Intelligence: A Modern Approach (AIMA)” (人工智能:现代方法) 是关于“守旧派” AI最好的一本书籍。这本书总体概述了人工智能领域,并解释了你需要了解的所有基本概念。

来自加州大学伯克利分校的 Artificial Intelligence course(人工智能课程)是一系列优秀的视频讲座,通过一种非常有趣的实践项目(训练AI玩Pacman游戏 )来解释基本知识。我推荐在视频的同时可以一起阅读AIMA,因为它是基于这本书,并从不同的角度解释了很多类似的概念,使他们更容易理解。它的讲解相对较深,对初学者来说是非常不错的资源。

大脑如何工作

如果你对人工智能感兴趣,你可能很想知道人的大脑是怎么工作的,下面的几本书会通过直观有趣的方式来解释最好的现代理论。

  1. Jeff Hawkins 的 On Intelligence有声读物

  2. Gödel, Escher, Bach

我建议通过这两本书入门,它们能很好地向你解释大脑工作的一般理论。

其他资源:

  1. Ray Kurzweil的 How to Create a Mind (如何创建一个头脑Ray Kurzweil) (有声读物).

  2. Principles of Neural Science (神经科学原理)是我能找到的最好的书,深入NS。 它谈论的是核心科学,神经解剖等。 非常有趣,但也很长 – 我还在读它。

四、数学

以下是你开始学习AI需要了解的非常基本的数学概念:

微积分学

  1. Khan Academy Calculus videos(可汗学院微积分视频)

  2. MIT lectures on Multivariable Calculus(MIT关于多变量微积分的讲座)

线性代数

  1. Khan Academy Linear Algebra videos(可汗学院线性代数视频)

  2. MIT linear algebra videos by Gilbert Strang(Gilbert Strang的MIT线性代数视频)

  3. Coding the Matrix (编码矩阵) – 布朗大学线程代数CS课程

概率和统计

  1. 可汗学院 Probability概率)与 Statistics统计)视频

  2. edx probability course (edx概率课程)

五、计算机科学

要掌握AI,你要熟悉计算机科学和编程。

如果你刚刚开始,我建议阅读 Dive Into Python 3 (深入Python 3)这本书,你在Python编程中所需要的大部分知识都会提到。

要更深入地了解计算机编程的本质 – 看这个经典的 MIT course (MIT课程)。这是一门关于lisp和计算机科学的基础的课程,基于 CS -结构和计算机程序的解释中最有影响力的书之一。

六、其他资源

  1. Metacademy  – 是你知识的“包管理器”。 你可以使用这个伟大的工具来了解你需要学习不同的ML主题的所有先决条件。

  2. kaggle  – 机器学习平台

原文链接

iOS技能图谱

原文:http://mp.weixin.qq.com/s/lpsrTGK6Dx748ozabeLVXw

前言

之前受 StuQ 之托,整理了iOS 技能图谱,分享给大家。大家如果觉得有什么推荐的,也可以留言回复。

我对技能图谱的价值持谨慎的态度,因为本身技术更新就比较快,图谱很难做到面面俱到和与时俱进,比如说最近有一个集成平台 https://buddybuild.com/ 就很火,我很难保证快速更新这些内容。

但是话说回来,有一个图谱,或许对一些人来说也是一个知识的查漏补缺的工具,看看也不会怀孕不是?

以下是图谱正文内容。

编程语言

Swift
Objective-C
C++/C
JavaScript

操作系统

Mac OSX
iOS
watchOS
tvOS
Linux 常用命令

开发基础

UI 控件
Storyboard & Xib
Core Data & Sqlite
Core Graphics
Animation
Block & GCD
Test Case 编写
Framework
Autolayout
手势识别,重力感应

开发进阶

引用计数
Runtime
Runloop
对象模型
Hybrid
沙盒机制
AVFoundation
Core Text
逆向与安全
class dump
IDA Pro
Hopper
iOS Class Guard

设计模式

MVC
MVVM
通知
代理
KVO
工厂模式
命令模式

函数式编程

ReactiveCocoa
RxSwift

开发工具

IDE
Xcode
AppCode
调试工具
Charles、Wireshark
Reveal
Instruments
插件
Alcatraz
VVDocument
XVim
FuzzyAutocompletePlugin
KSImageNamed-Xcode
辅助工具
xScope
ImageOptim
马克鳗
Dash
Deploymate
FauxPas
PaintCode
命令行工具
xcodebuild、xcode-select
nomad
xctool
fastlane
持续集成
Jenkins
Travis CI
Bot

开源项目

AFNetworking & Alamofire
Masonry
SDWebImage
SwiftyJSON
JSPatch
React Native

包管理

CocoaPods
Carthage
Swift Package Manager

App 打包上传与审核

Apple Developer 网站
iTunes Connect 网站
IAP
加急审核申请
打包脚本

第三方服务

崩溃收集
Bugly
Crashlytics
BugHD
数据统计
Google Analytics
友盟
MTA
Flurry
App Annie
应用分发
TestFlight
蒲公英
FIR

Flex布局

弹性和模型(1-5给父级)
①在使用弹性和模型的时候,父元素必须要加display:box;或者display:inline-block;
新版弹性盒模型加在父级的:display:flex;移动端部分机型不支持;
旧版弹性盒模型加在父级的:display:-webkit-box;
②主轴设置同x轴  垂直x的叫侧轴
定义盒模型的主轴方向(左–右)
新版:flex-direction:row(水平方向)|column(垂直方向);
旧版:-webkit-box-orient:horizontal(水平方向)|vertical(垂直方向);
③主轴上的元素排列顺序
新版:flex-direction:row-reverse(设置主轴方向为水平 ,元素排列顺序为反序顶着右边 )|column-reverse(设置主轴方向为垂直 ,元素排列顺序为反序,顶着底部 );
旧版:(水平)-webkit-box-orient:horizontal;-webkit-box-direction:normal(正序)|reverse(顶着左侧反序);
(垂直)-webkit-box-orient:vertical;-webkit-box-direction:normal(正序)|reverse(顶着顶侧反序);
④主轴方向富余空间的管理
新版:justify-content:flex-start(元素在主轴开始位置,富余空间在主轴的结束位置)|flex-end(元素在主轴结束位置,富余空间在主轴的开始位置 )|center(元素在主轴中间,富余空间在主轴两侧)|space-between(富余空间平均分配在每两个元素之间)|space-around(富余空间平均分配在每个元素的两侧)
旧版:-webkit-box-pack:start(元素在主轴开始位置,富余空间在主轴的结束位置 )|end(元素在主轴结束位置,富余空间在主轴的开始位置 )|center(元素在主轴中间,富余空间在主轴两侧 )|justify(富余空间平均分配在每两个元素之间)
⑤侧轴方向富余空间管理
新版:align-item:flex-start(元素在侧轴开始位置,富余空间在侧 轴的结束位置 )|flex-end(元素在侧 轴结束位置,富余空间在侧 轴的开始位置 )|center(元素在侧 轴中间,富余空间在侧 轴两侧 )|baseline(根据侧轴方向上文字基线对其)
旧版:-webkit-box-align:start(元素在侧轴开始位置,富余空间在侧 轴的结束位置 )|end(元素在侧 轴结束位置,富余空间在侧 轴的开始位置 )|center(元素在侧 轴中间,富余空间在侧 轴两侧 )
⑥盒子的弹性空间(从现在开始加给子级)
新版:flex-grow:1;
旧版:-webkit-box-flex:1;
子元素的尺寸=盒子的尺寸*子元素的box-flex属性值/所有子元素的box-flex的属性值的和
⑦元素的具体位置设置
新版:order:数字(数值越小越靠前可以接受0和负值)
旧版:-webkit-box-ordinal-group:数字(数值越小越靠前 ,负值和0最小值默认处理为1,)

JavaScript匿名函数的写法

// 最常用的两种写法
(function(){ /* code */ }()); // 老道推荐写法
(function(){ /* code */ })(); // 当然这种也可以
 
// 括号和JS的一些操作符(如 = && || ,等)可以在函数表达式和函数声明上消除歧义
// 如下代码中,解析器已经知道一个是表达式了,于是也会把另一个默认为表达式
// 但是两者交换则会报错
var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();
 
// 如果你不怕代码晦涩难读,也可以选择一元运算符
!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();
 
// 你也可以这样
new function(){ /* code */ }
new function(){ /* code */ }() // 带参数

为了提高代码可读性,我们推荐这个写法也加上():
var i = function(){ return 10; }();
改为
var i = (function(){ return 10; }());

IIS支持JSP(Windows2003下IIS6.0配置Tomcat8.0支持JSP)

如果是IIS7看这里:IIS7支持JSP(IIS7将80端口转发到Tomcat)

如果需要Windows2003支持JSP,我们并不需要通过 IIS,只需要安装 Tomcat 即可。
但是,因为 IIS 已经占用了 80 端口,那么我们在访问 Tomcat 的时候每次都要带上:8080或者其他端口号。
下面我们将实现如何让 Tomcat 和 IIS 共享 80 端口。

1 为了支持Java,下载JDK并安装,我选了1.8.0版本:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

2 为了支持JSP,下载Tomcat并安装,我选了8.0版本:http://tomcat.apache.org/download-80.cgi

3 为了连接IIS和Tomcat,下载isapi_redirect并安装,我选了1.2.14版本:http://archive.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/win32/jk-1.2.14/

4 安装isapi_redirect之后,打开 IIS 网站列表,排在第一个的网站,右键-属性-ISAPI筛选器,会出现一个jakarta,指向 isapi_redirect 安装目录的 dll 文件,该网站下面会出现一个虚拟目录 jakarta。我们不管这个,因为通常我们要新建一个站点。

5 新建一个IIS站点,主目录随意,允许读取、运行脚本和执行。然后为该站点添加虚拟目录,名称必须是 jakarta,目录指向“isapi_redirect安装目录\bin\”。在该站点上右键-属性-ISAPI筛选器,添加筛选器,名字必须是 jakarta,执行文件在“isapi_redirect安装目录\bin\isapi_redirect.dll”。

6 添加一个 web 扩展,命名 jakarta,添加“isapi_redirect安装目录\bin\isapi_redirect.dll”,并勾选启用。

7 添加 .jsp 映射,执行文件“isapi_redirect安装目录\bin\isapi_redirect.dll”。

8 isapi_redirect 有两个重要的配置文件,在安装目录的 conf 文件夹,workers.properties.minimal 使用默认配置。修改 uriworkermap.properties 文件,在末尾添加一行 /*=wlb。

9 重启 IIS 和 Tomcat,完工。

如有疑问可以联系我

stopPropagation(),preventDefault()和return false

阅读本文之前你最好了解JavaScript在浏览器中 事件冒泡事件捕获 两个概念。

因为HTML结构中存在着父、子节点的事件冒泡,以及浏览器默认动作,所以我们使用 JavaScript 时为了达到预期效果经常需要阻止事件和动作执行. 一般我们会用到三种方法, 分别是 stopPropagation(),preventDefault() 和 return false。它们之间有什么区别,该怎么使用呢?将在本文中进行讲解。

名词解释

监听事件:指在节点上能被监听的页面操作。如: select 节点的 change 事件, a 节点的 click 事件。
浏览器默认动作:指特定页面元素上带有的功能。如: 点击 a 链接节点的跳转动作, 表单提交动作。
stopPropagation():阻止事件冒泡。
preventDefault():阻止浏览器默认行为。

stopPropagation()

因为事件可以在各层级的节点中传递,不管是冒泡还是捕获,有时我们希望事件在特定节点执行完之后不再传递,可以使用事件对象的 stopPropagation() 方法。
假设页面上存在一个浮动弹出层,显示在最前面,当点击弹出层以外页面区域时,隐藏弹出层。为了做到这样的效果,我们会监听 documentElement 的 click 事件,一旦事件被触发即隐藏弹出层。但是…
这显然存在问题。当用户点击弹出层时,我们不希望它隐藏掉。但因为事件的冒泡传递,documentElement 的 click 事件也会被触发。这个时候,我们可以监听弹出层的 click 事件,并使用 stopPropagation() 方法阻止冒泡。请参考下面的代码:


// 在弹出对话框上点击时, 不进行任何页面操作, 并阻止冒泡
document.getElementById('dialog').onclick = function(ev) {
    ev.stopPropagation();
};
// 在 documentElement 节点上监听到点击事件时, 隐藏对话框
document.documentElement.onclick = function(ev) {
    document.getElementById('dialog').style.display = 'none';
};

stopPropagation() 相当好用,可是 IE8 及以前版本都不支持(用jquery就不用考略这点了)。IE 的事件对象包含特有的属性 cancelBubble, 只要将它赋值为 false 即可阻止事件继续。如:


// 在弹出对话框上点击时, 不进行任何页面操作, 并阻止冒泡
document.getElementById('dialog').onclick = function(ev) {
    ev.cancelBubble = false;
};

preventDefault()

一个带事件监听的链接代码如下:


<a href="http://w3c.org" onclick="alert('my name is chic!');">点击我哦!</a>

点击该链接, 显示对话框后跳转页面。由此可知,除了执行监听事件还会触发浏览器默认动作;执行监听事件在前,触发浏览器默认动作在后。
这里有个经典示例, 我们希望点击链接在新窗口打开页面,但不希望当前页面跳转。这个时候可以使用 preventDefault() 阻止后面将要执行的浏览器默认动作。

<a id="link" href="http://w3c.org">W3C 首页链接</a>
<script>
    //在新窗口, 打开页面
    document.getElementById('link').onclick = function(ev) {
        // 阻止浏览器默认动作 (页面跳转)
        ev.preventDefault();
        // 在新窗口打开页面
        window.open(this.href);
    };
</script>

return false

退出执行,return false 之后的所有触发事件和动作都不会被执行。有时候 return false 可以用来替代 stopPropagation() 和 preventDefault(),比如我们上面新窗口打开链接的例子,如:

<a id="link" href="http://w3c.org">W3C 首页链接</a> 
<script>
    //在新窗口, 打开页面
    document.getElementById('link').onclick = function(ev) {
        // 在新窗口打开页面
        window.open(this.href);
        // 退出执行 (在监听事件之后执行的浏览器默认动作将不会被执行)
        return false;
    };
</script>

有人认为 return false = stopPropagation() + preventDefault(),其实是错的。return false 不但阻止事件,还可以返回对象,跳出循环等… 方便地一刀切而不够灵活,滥用易出错.

“return false”之所以被误用的如此厉害,是因为它看起来像是完成了我们交给它的工作,浏览器不会再将我们重定向到 href 中的链接,表单也不会被继续提交,但这么做到底有什么不对呢?

可能在你刚开始学习关于jQuery事件处理时,看到的第一个例子就是关于如何阻止浏览器执行默认行为,比如下面这段演示click事件的代码

$("a.toggle").click(function () {  
    $("#mydiv").toggle();  
    return false; // Prevent browser from visiting `#`  
}); 

这个函数使用toggle来显示或者隐藏 #mydiv,然后阻止浏览器继续访问href中指定的链接。

像上面这样的例子会让用户养成使用“return false”来阻止浏览器执行默认行为的坏习惯,在这篇文章里,我将会讨论关于阻止浏览器执行默认行为的两个非常重要的主题:

选择正确的方法:return false还是preventDefault,stopPropagation或者stopImmediatePropagation
选择合适的位置,开始,结束,还是中间某个地方:你应该在事件回调的哪个部分取消浏览器执行默认行为?

注意:当我在这篇文章中提到event bubbling(事件冒泡),我想表达的是大部分事件都是先在初始DOM上触发,然后再通过DOM树往上,在每一级父元素上触发,事件不会在兄弟节点或是子节点上冒泡(当事件向下冒泡时,我们叫它事件捕捉(event capturing)),你可以在这里了解更多关于事件起泡和捕捉的介绍。

选择正确的方法

“return false”之所以被误用的如此厉害,是因为它看起来像是完成了我们交给它的工作,浏览器不会再将我们重定向到href中的链接,表单也不会被继续提交,但这么做到底有什么不对呢?

”return false“到底做了什么?

当你每次调用”return false“的时候,它实际上做了3件事情:

  1. event.preventDefault();
  2. event.stopPropagation();
  3. 停止回调函数执行并立即返回。

“等等”,你叫了起来!我只是想让浏览器停止继续执行默认行为而已,我不需要它去做另外2件事。

这3件事中用来阻止浏览器继续执行默认行为的只有preventDefault,除非你想要停止事件冒泡,否则使用return false会为你的代码埋下很大的隐患,让我们通过一个真实的例子来看看这样的误用会造成什么后果:

这是我们用来演示的HTML:

<div class="post">  
<h2><a href="http://jb51.net">My Page</a></h2>  
<div class="content">  
    Teaser text...  
  </div>  
</div>  
<div class="post">  
<h2><a href="http://jb51.net">My Other Page</a></h2>  
<div class="content">  
    Teaser text...  
  </div>  
</div>

现在假设我们想要在用户点击文章标题时,将文章动态载入到div.contentd中:

jQuery(document).ready(function ($) {  
  $("div.post h2 a").click(function () {  
    var a    = $(this),  
    href = a.attr('href'), // Let jQuery normalize `href`,  
    content  = a.parent().next();  
    content.load(href + " #content");  
    return false; // "cancel" the default behavior of following the link  
  });  
});

这段代码可以正常工作(至少目前是),但如果我们顺着这个思路继续,如果我想要在用户点击了一个div.post元素(或者任何一个它的子元素)时,给它加上一个active类,我就需要给div.post增加了一个click回调:

// Inside Document Ready:  
var posts = $("div.post");  
  posts.click(function () {  
  // Remove active from all div.post  
  posts.removeClass("active");  
  // Add it back to this one  
  $(this).addClass("active");  
});

现在,如果我们点击一个帖子的标题,这段代码会工作吗?答案是不会,因为我们在标题的click回调里使用了return false而不是我们应该使用的,”return false“等于event.preventDefault();加event.stopPropagation();,所以事件冒泡就被终止了,click事件不会被冒泡到div.post上,我们为它添加的事件回调当然也就不会被调用了。

如果我们把它和live或者delegate事件混在一起使用时,情况就更糟了。

$("a").click(function () {  
  // do something  
  return false;  
});  

$("a").live("click", function () {  
  // THIS WON'T FIRE  
});

那么我们真正需要的是什么呢?

preventDefault()

大多数情况下,当你使用return false时,你其实真正需要的是e.preventDefault()。要使用e.preventDefault,你需要确保你传递了event参数到你的回掉函数中(在这个例子里,就是那个e):

$("a").click(function (e) {  
  // e == our event data  
  e.preventDefault();  
});

它会替我们完成所有工作,但不会阻止父节点继续处理事件,要记住,你放在代码中的限制越少,你的代码就越灵活,也就越易于维护。

stopPropagation()

但有些情况下,你有可能需要停止事件冒泡,让我们看看下面的例子:

<div class="post">  
    Normal text and then a <a href="http://jb51.net">link</a> and then more text.  
</div>

现在,让我们假设如果你点了div上除了a链接之外的地方,我们希望能发生点什么事情(比如改变下背景什么的),但是不能影响用户点击a链接的行为(从可用性的角度,这个例子不怎么好,你可能不希望用户点击别的地方时发生任何事情)。

$("div.post").click(function () {  
  // Do the first thing;  
});  

$("div.post a").click(function (e) {  
  // Don't cancel the browser's default action  
  // and don't bubble this event!  
  e.stopPropagation();  
});

在这种情况下,如果我们使用return false,div的click事件不会被触发,但是用户也不会到达他们点的那个链接。

stopImmediatePropagation()

这个方法会停止一个事件继续执行,即使当前的对象上还绑定了其它处理函数,所有绑定在一个对象上的事件会按绑定顺序执行,看看下面的例子:

$("div a").click(function () {  
  // Do something  
});  

$("div a").click(function (e) {  
  // Do something else  
  e.stopImmediatePropagation();  
});  

$("div a").click(function () {  
  // THIS NEVER FIRES  
});  

$("div").click(function () {  
  // THIS NEVER FIRES  
});

你可能会觉得这个例子看起来很别扭,没错,尽管如此,但有时这的确会发生,如果你的代码非常复杂,那么不同的widgets和plugin就有可能在同一个对象上添加事件,如果遇到这种情况,那你就很有必要理解和使用stopImmediatePropagation。

return false

只有当你同时需要preventDefault和stopPropagation,并且你的代码可以接受直到你的回调执行完成才停止执行浏览器的默认行为,那你就可以使用”return false“。但我强烈建议你别在写给其它jQuery开发者的演示代码中使用这个方法,因为这会造成更多误用,只有在你确信非用不可的情况下再去使用”return false“。

选择适当的位置

如果你使用了”return false“,它只会在你的回调函数执行结束才去取消浏览器的默认行为,但是使用e.preventDefault,我们有更多的选择,它可以随时停止浏览器执行默认动作,而不管你将它放在函数的哪个部分。

1. 开发阶段,你应该总是将它放在第一行。你最不想做的事情可能就是你正在调试将一个form改成ajax提交的时候,它却已经被按照老方法提交了。

2. 产品阶段,如果你采用了渐进增强(progressive enhancement),那就把它放到回调的结束位置,或者是逻辑终点,如果在一个普通页面采用渐进增强,那你就需要在服务器端考虑如果浏览器不支持JS时(或者被禁用时),对链接的click事件和表单的提交事件的处理。这里的好处是,我们不考虑关闭JS的情况,只考虑支持js时的强狂,如果你的回调代码出错抛出了异常,让我们看看下面的代码:

var data = {};  
$("a").click(function (e) {  
  e.preventDefault(); // cancel default behavior  
  // Throws an error because `my` is undefined  
  $("body").append(data.my.link);  
  // The original link doesn't work AND the "cool"  
  // JavaScript has broken. The user is left with NOTHING!  
});

现在,让我们看看同样的事件,把preventDefault调用放在底部的效果:

var data = {};  
$("a").click(function (e) {  
  // Throws an error because `my` is undefined  
  $("body").append(data.my.link);  

  // This line is never reached, and your website  
  // falls back to using the `href` instead of this  
  // "cool" broken JavaScript!  
  e.preventDefault(); // cancel default behavior  
});

这对表单提交也同样有效,你可以更好的应对出错的情况,别指望你的代码一直正常工作,在发生错误时有正确的应对总胜过假设代码不会出错。

3.在产品阶段,如果功能这设计JS,那就还应该放在第一行。

记住,不必非得是函数的第一行,但是越早越好,这里的原则是:如果函数的功能是通过JS实现的(不涉及服务端交互),那就没必要考虑兼容,在这种情况下,添加在第一行可以防止URL中出现#字符,但显然,你还是应该尽可能多的增加些错误处理代码,以防止用户在出错时变得不知所措。


TOP