GithubHelp home page GithubHelp logo

article's People

Contributors

ccchangkong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

article's Issues

CSS兼容攻略

blog

平常要多留心,摸不准兼容如何就该多看看can i use,额,还有就是自己要明白页面该兼容到什么程度

1 是否需要兼容

一上来得把这个问题想好,有些效果不兼容就不兼容呗,只要后退平稳即可,

如这种情况下的CSS Shapes:

图片来自w3cplus,这种情况下,对于不支持CSS Shapes属性的浏览器,还是不用强行支持的好。

2 是否只需后退处理即可

跟第一点比就是加上额外的后退处理(本来就该有的),如CSS渐变的后退处理:

    background-color: #f9efee;
    background-image: linear-gradient(to left, #f5e5e3 0%, #ffffff 52%, #f5e5e3 100%);

3 需要额外区别的情况

用css处理的话就是各种HACK了:

CSS hack技巧大全

巧用浏览器CSS属性值的不兼容向下兼容hack技巧

用JS处理的话,最好的方法自然是能力判断了,可以使用modernizr.js或如下代码:

if ( !('shape-margin' in document.documentElement.style)) {}
//如果不支持shape-margin属性则如何如何

4 强行效果一样

到了这一步,那只能拿出这种代码了

text-shadow: 2px 2px 15px #333;
filter: glow(color=#333333, strength=2);
/*老IE不支持文字阴影,对其使用IE滤镜*/

又多又杂,还是看这吧

自己捣鼓的一套基于gulp的工作流

自己捣鼓的一套基于gulp的工作流

git地址
http://www.vastskycc.com/?id=20

目录结构

package.json注释

{
  "name": "gulp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "gulp dev",//npm run dev
    "build": "gulp build",//npm run build
    "upload": "gulp upload",//npm run upload
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "browser-sync": "^2.15.0",//监听改动刷新
    "gulp": "^3.9.1",//gulp
    "gulp-autoprefixer": "^3.1.1",//补齐css浏览器前缀
    "gulp-clean": "^0.3.2",//删除文件
    "gulp-clean-css": "^2.0.12",//css压缩,gulp-minify-css不更新了
    "gulp-concat": "^2.6.0",//合并文件
    "gulp-ftp": "^1.1.0",//提交到ftp服务器
    "gulp-git": "^1.11.3",//提交到git,这个我没写
    "gulp-htmlmin": "^2.0.0",//压缩html
    "gulp-imagemin": "^3.0.3",//压缩图片
    "gulp-rename": "^1.2.2",//重命名文件
    "gulp-rev-append": "^0.1.6",//添加MD5版本号,这里改成了时间戳
    "gulp-sass": "^2.3.2",//编译SASS
    "gulp-uglify": "^2.0.0",//js压缩
    "gulp-util": "^3.0.7"//说是ftp配套用的,看了下是包装了些常用函数
  }
}

gulpfile.js注释

引入部分

let gulp = require('gulp');
// 引入组件
let browserSync = require('browser-sync').create(), //监听刷新
    reload = browserSync.reload,
    ftp = require('gulp-ftp'), // ftp上传
    gutil = require('gulp-util'),
    sass = require('gulp-sass'), // sass
    cleancss = require('gulp-clean-css'), // CSS压缩
    autoprefixer = require("gulp-autoprefixer"),
    uglify = require('gulp-uglify'), // js压缩
    concat = require('gulp-concat'), // 合并文件
    rename = require('gulp-rename'), // 重命名
    clean = require('gulp-clean'), //清空文件夹
    imagemin = require('gulp-imagemin'), //压缩图片
    rev = require('gulp-rev-append'), //添加MD5
    htmlmin = require('gulp-htmlmin'); // 压缩html
// git = require('gulp-git'),              //git

开发构建流程

//dev
gulp.task('sass:dev', () => {
    gulp.src('src/sass/*.scss')
        .pipe(sass())
        .pipe(gulp.dest('src/css/'))
        .pipe(reload({ stream: true }))
});
// 合并、重命名css
gulp.task('css:dev', ['sass:dev'], () => {
    gulp.src(['src/css/*.css', '!src/css/areaMap.css'])
        .pipe(concat('all.css'))
        .pipe(gulp.dest('dist/css/'))
});
// 合并、重命名js
gulp.task('js:dev', () => {
    gulp.src('src/js/*.js')
        .pipe(concat('all.js'))
        .pipe(gulp.dest('dist/js/'))
        .pipe(reload({ stream: true }))
});
gulp.task('html:dev', () => {
    gulp.src('src/tpl/*.html')
        .pipe(gulp.dest('dist'))
});
// 将bower的库文件对应到指定位置
gulp.task('carry', () => {
    gulp.src('/src/brower/*')
        .pipe(gulp.dest('/dist/brower/'));
    // gulp.src('/src/img/*')
    //     .pipe(gulp.dest('/dist/img/'));
});

//开发构建
gulp.task('dev', ['css:dev', 'js:dev', 'html:dev', 'carry'], () => {
    browserSync.init({
        server: {
            baseDir: "dist" // 设置服务器的根目录为dist目录
        },
        notify: false // 开启静默模式
    });
    // 我们使用gulp的文件监听功能,来实时编译修改过后的文件
    gulp.watch('src/js/*.js', ['js:dev']);
    gulp.watch('src/sass/*.scss', ['sass:dev']);
    gulp.watch('src/tpl/*.html', ['html:dev']);
});

正式构建

// build
// sass解析
gulp.task('sass', () => {
    gulp.src('src/sass/*.scss')
        //输出为压缩
        // .pipe(sass({
        //     outputStyle: 'compressed'
        // }))
        .pipe(sass())
        .pipe(gulp.dest('src/css/'))
});
// 合并、压缩、重命名css
gulp.task('css', ['sass'], () => {
    gulp.src(['src/css/*.css', '!src/css/areaMap.css'])
        .pipe(concat('all.css'))
        .pipe(autoprefixer({
            browsers: ['last 2 versions', 'Android >= 4.0'],
            cascade: true, //是否美化属性值 默认:true 像这样:
            remove: true //是否去掉不必要的前缀 默认:true 
        }))
        .pipe(cleancss())
        .pipe(gulp.dest('dist/css'));
});
// 合并,压缩,重命名js文件
gulp.task('js', () => {
    gulp.src('src/js/*.js')
        .pipe(concat('all.js'))
        .pipe(uglify())
        .pipe(gulp.dest('dist/js'));
});
gulp.task('html', () => {
    gulp.src('src/tpl/*.html')
        .pipe(rev())//记得在引用地址后面加后缀,插件原本是ver=@@hash ,这里改成了v=@@hash
      //<link rel="stylesheet" href="css/all.css?v=@@hash">
      //<script src="js/all.js?v=@@hash"></script>
        .pipe(htmlmin({
            removeComments: true, //清除HTML注释
            collapseWhitespace: true, //压缩HTML
            collapseBooleanAttributes: true, //省略布尔属性的值 <input checked="true"/> ==> <input />
            removeEmptyAttributes: true, //删除所有空格作属性值 <input id="" /> ==> <input />
            removeScriptTypeAttributes: true, //删除<script>的type="text/javascript"
            removeStyleLinkTypeAttributes: true, //删除<style>和<link>的type="text/css"
            minifyJS: true, //压缩页面JS
            minifyCSS: true //压缩页面CSS
        }))
        .pipe(gulp.dest('dist'))
});

gulp.task('img', () => {
    gulp.src('src/img/*.{png,jpg,gif,ico}')
        .pipe(imagemin({
            optimizationLevel: 5, //类型:Number  默认:3  取值范围:0-7(优化等级)
            progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片
            interlaced: true, //类型:Boolean 默认:false 隔行扫描gif进行渲染
            multipass: true //类型:Boolean 默认:false 多次优化svg直到完全优化
        }))
        .pipe(gulp.dest('dist/img'));
});
// 清空图片、样式、js
gulp.task('clean', () => {
    gulp.src('dist', { read: false })
        .pipe(clean({ force: true }));
});
// 将bower的库文件对应到指定位置
gulp.task('carry', () => {
    gulp.src('/src/brower/*')
        .pipe(gulp.dest('/dist/brower/'));
    // gulp.src('/src/img/*')
    //     .pipe(gulp.dest('/dist/img/'));
});


//正式构建
gulp.task('build', ['clean','css', 'js', 'img', 'html', 'carry']);

上传

gulp.task('upload', () => {
    gulp.src('dist/**')
        .pipe(ftp({
            host: '8.8.8.8', // 远程主机ip
            port: 22, // 端口
            user: 'username', // 帐号
            pass: 'password', // 密码
            remotePath: '/project' // 上传路径,不存在则新建
        }))
        .pipe(gutil.noop())
})

gulp-rev-append的坑

之前还改过这个的正则

//var FILE_DECL = /(?:href=|src=|url\()['|"]([^\s>"']+?)\?ver=([^\s>"']+?)['|"]/gi;
var FILE_DECL = /(?:href=|src=|url\()['|"]([^\s>"']+?)\?v=([^\s>"']+?)['|"]/gi;

ver->v

这个插件还有有些问题。。。

一是一行代码只能替换一次,html如果压缩过了,则只能替换一个;

解决办法:在html压缩之前进行替换。

二是如果引入的路径为构建的路径,在当前目录下找不到资源的时候该插件无法使用;

解决办法:修改源码,不使用hash算法,直接使用时间戳作为版本号。

try {
         data = fs.readFileSync(dependencyPath);
         hash = crypto.createHash('md5');
         hash.update(data.toString(), 'utf8');
          var _rev=new Date().getTime();
          line = line.replace(groups[2], _rev);
        }

修改为

 try {
          var _rev=new Date().getTime();
          line = line.replace(groups[2], _rev);
        }

参考资料

一点 gulp教程

gulp改造gulp-rev-append插件实现资源文件链接自动添加MD5版本号

gulp-rev-append Issues

同域下通过地址带参传递

场景描述;通过点击此页面的一堆链接,来跳转到另外的一个文章页所对应的内容,此例中为点击‘表把’。

blob.png

blob.png

直接上代码!

第一个页面上的。

<a href="indexT.html#a11" target="_blank">定期保养</a>
<a href="indexT.html#a12" target="_blank">手表进水</a>

第二个页面接收

$("dd").first().css('border-left', '1px solid #c40000');
// alert(window.location.hash);
var h = window.location.hash.split('#')[1];
 // alert(window.location.href.split('#')[1]);
if (h) {
$("[class=" + h + "]").css('border-left', '1px solid #c40000').siblings().css('border-left', '');
$("[tclass=" + h + "]").css("display", "block").siblings().css("display", "none");
};

在开头的例子中,window.location.hash与window.location.href分别为#a24与file:///D:/demo/sbsh/indexT.html#a24,因为内容不多,所以用split函数以‘#’来分割结果一样,但用hash更妥当,毕竟本来就是用来干这的- -

切图仔的一点工作经验

blog

切图仔的一点工作经验

0 磨刀不误砍柴功

一个趁手的编辑器(比如自己调教好的st3)或IDE(如Hbuilder、webstorm),可以极大的提高自己的工作效率。

调试功能强大、实现规范标准的浏览器(Chrome!)。

明确浏览器兼容要求,能用啥该用啥想想清楚。

一台足够快的电脑。

多学多做,代码基础扎实。

1 PSD到手之后

从美工那拿到PSD文件后,先别急着敲,仔细看一遍,想想html结构该怎么建,哪些元素可以重用,一些效果该怎么实现。

如果美工做图的时候能把什么命名字体行距内外边距标注清楚,能省切图仔多少事

2 编写HTML

先创建好模板,常用的meta标签、重置css这些先带上。

接下来写页面的框架,定好几个大的区块,往里面填具体的结构,注意结构要清晰明确语义化。

3 编写CSS

类名

我个人倾向于BEM命名法,当然,我现在水平不够,不太纯

写css的时候,靠着IDE智能提示,那感觉,特爽!

伪元素

装饰性元素可以尽量用伪元素来实现,什么小图标小线段序号(使用css计数器)之类的东西,可以有效减小HTML文档的复杂度。

杂七杂八的一些技巧

高宽不定死

减少计算量,方便更改

少用浮动

如果不是非要兼容IE7,那使用inline-block可以替掉很多需要使用float的场景。

少用浮动,那由浮动引起的一些问题也就没有了,像什么破坏DOM流。

必要的回退

/*先写回退,再写高级样式*/
background-color: #fff;
background-color: rgba(256, 256, 256, 0.48);

使用高级选择器

很多情况下都不用js啦!

慢慢补充

结尾

vue-cli常用设置

vue-cli常用设置

基于vue-cli做了好几个项目了,想把一些自己的常用设置写出来,磨了好久,一看vue-cli3.0都快出来了,不能再磨了。。

路径相关

css内引用的资源

build -> utils.js

  // generate loader string to be used with extract text plugin
  function generateLoaders (loader, loaderOptions) {
	//less

    // Extract CSS when that option is specified
    // (which is the case during production build)
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        publicPath: '../../', //注意: 此处根据路径, 自动更改
        fallback: 'vue-style-loader'
      })
    } else {
      return ['vue-style-loader'].concat(loaders)
    }
  }

本地访问

config -> index.js

module.exports = {
  build: {
	//less
    //assetsPublicPath: '/',
    assetsPublicPath: './',
	//less
  },
  //less
}

调试相关

内网访问

config -> index.js

module.exports = {
  //less
  dev: {
    //less
    port: process.env.PORT || 8080,//可改端口
    host:'192.168.0.105',//不是8080端口可能需要指定host为本机IP
  }
}

跨域代理

config -> index.js

module.exports = {
  //less
  dev: {
    //less
    proxyTable: {
      '/AppHome': {
        target: 'http://192.168.0.211:2334',//接口域名
        changeOrigin: true,//是否跨域
        pathRewrite: {
          '^/AppHome': '/AppHome'//需要rewrite重写
        }
      }
    },
  }
}
config -> dev.env.js
module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  API_HOST: '"AppHome/"' 
})

config -> prod.env.js
module.exports = {
  NODE_ENV: '"production"',
  API_HOST: '"http://xxx.xxx.com/AppHome/"' //生产环境改为绝对地址,免得路径错了
}

//调用
this.$http
    .post(process.env.API_HOST + "GetApproveTypeList", { ID: 0 })
    .then(data => {
    let $data = data.data;
    if ($data.IsSuccess) {
        this.list.push(...$data.Model);
    }
});

路由加载切换

异步加载可以加快首屏加载速度,但是在开发阶段会导致热加载变慢,所以根据NODE_ENV来判断,开发环境不使用异步

let _import
if (process.env.NODE_ENV === 'development') {
  _import = file => require('@/components/' + file + '.vue').default
}
if (process.env.NODE_ENV === 'production') {
  _import = file => () => import('@/components/' + file + '.vue')
}

routes: [
    {
        path: '/',
        name: 'Index',
        component: _import('Approve/Index'),
        meta: {
            level: 1
        }
    },
]

打包

dll打包

1、在build目录新建webpack.dll.conf.js

var path = require("path");
var webpack = require("webpack");

module.exports = {
    // 你想要打包的模块的数组
    entry: {
        vendor: ['vue/dist/vue.esm.js', //有些资源需要直接指定js,否则会重复打包
                 'vuex',
                 'axios',
                 'vue-router'
                ]
    },
    output: {
        path: path.join(__dirname, '../static/js'), // 打包后文件输出的位置
        filename: '[name].dll.js',
        library: '[name]_library'
        // vendor.dll.js中暴露出的全局变量名。

    },
    plugins: [
        new webpack.DllPlugin({
            path: path.join(__dirname, '..', '[name]-manifest.json'),
            name: '[name]_library',
            context: __dirname
        }),
        // 压缩打包的文件
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ]
};

2、在build目录下的webpack.prod.conf.js添加新插件

const webpackConfig = merge(baseWebpackConfig, {
   //less
  plugins: [
    //less
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('../vendor-manifest.json')
    })
  ]
})

3、在项目根目录下的index.html内添加dll.js引用

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script src="./static/js/vendor.dll.js"></script>
  </body>
</html>

4、在项目根目录下的package.json内添加dll命令(顺便给build命令添加report),运行一次生成dll.js

  "scripts": {
    "dev": "node build/dev-server.js",
    "start": "npm run dev",
    "build": "node build/build.js --report",
    "dll": "webpack --config build//webpack.dll.conf.js"
  }

关闭SourceMap

config -> index.js

module.exports = {
  //less
  build: {
    //less
    productionSourceMap: false,
  },
}

基于vue+muse-ui的简历生成器

基于vue+muse-ui的简历生成器

项目地址

预览

blog

项目说明

由webpack构建,vue2.0驱动,muse-ui样式库搭建,组件化开发,localstrong存储,h2ml2canvas导出图片,jquery free

项目结构

vue-cli搭建的模板,分了两个组件

踩的坑

html2canvas

这玩意有点坑

对css属性展现有问题

border-radius属性不知为何,截得的角度只有一半(设置50%的话截出来就25%),后来设置了100%居然成了;

linear-gradient渐变显示不出来,postion也不对,不管了;

opacity、visibility设置透明没用。

图片跨域的问题

可以用官方提供的各种代理来解决,我这边选择了本地图片转base64存localstrong

异步的问题

在生成图片前进行操作,可能顺序会出问题,解决方法是把html2canvas方法放在this.$nextTick()里

兼容有问题

vue

这个主要是自己知识水平不够

作用域

window.alert(),this.data什么的

组件通信

父到子props,子到父events,如果有多层组件的话应该用vuex统一管理

不足

1.兼容没处理好,chrome、ff测试没问题,IE11和EDGE截图功能出错;

2.组件拆分不够,两个大模块确实不好看;

3.没用vuex,数据通信有点乱;

4.代码解耦不完全。

以上!

千岁我老婆~

webkit内核下,字号会自动放大的问题

webkit内核下,字号会自动放大的问题

blog

问题描述

有时候在做移动端页面的时候,会发现某些字体会自动放大(而页面中另一部分却是对的- -),放大的比例看上去还没啥规律,很是诡异。

一探究竟

网上一通搜索,还真搜出来了。

首先,这个不是bug,是Chromium内核提高移动端文本可读性的一个特性,叫做这个特性被称做「Text Autosizer」,又称「Font Boosting」、「Font Inflation」,具体可以见这个文档Chromium’s Text Autosizer,计算规则则可以在这里看到TextAutosizer.cpp,在文档里可以看到计算公式如下

multiplier = Math.max(1, deviceScaleAdjustment * textScalingSlider * systemFontScale * clusterWidth / screenWidth);
if (originFontSize < 16) {
    computedFontSize = originFontSize * multiplier;
}
else if (16 <= originFontSize <= (32 * multiplier - 16)) {
    computedFontSize = (originFontSize / 2) + (16 * multiplier - 8);
}
else if (originFontSize > (32 * multiplier - 16)) {
    computedFontSize = originFontSize;
}

变量解释:

  • originFontSize: 原始字体大小
  • computedFontSize: 经过计算后的字体大小
  • multiplier: 换算系数,值由以下几个值计算得到deviceScaleAdjustment:
    当指定 viewport width=device-width 时此值为 1,否则值在 1.05 - 1.3 之间,有专门的计算规则
    textScalingSlider: 浏览器中手动指定的缩放比例,默认为 1
    systemFontScale: 系统字体大小,Android设备可以在「设备 - 显示 - 字体大小」处设置,默认为 1
    clusterWidth: 应用 Font Boosting 特性字体所在元素的宽度(如何确定这个元素请参考上边两个链接)
    screenWidth: 设备屏幕分辨率(DIPs, Density-Independent Pixels),如 iPhone 5 为 320

解决问题

其实嘛,解决起来还是容易的~

给元素指定宽高

试了下给元素单独设置widthheightmax-height即可禁用Text Autosizer

使用-webkit-text-size-adjust

给元素设置-webkit-text-size-adjust: none;可禁用Text Autosizer,这个属性还能使得我们在移动端使用小于12px的字体。此属性在桌面版中无效。

参考资料

flexible.js字体大小诡异现象解析及解决方案

网页字体缩放样式-webkit-text-size-adjust的用法详解

以上。

new game!

基于vue的音乐播放器

基于vue的音乐播放器

blog
项目地址
预览

项目说明

由webpack构建,基于vue全家桶,模块化开发,调用了qq音乐的接口



项目结构

<template>
  <div id="app">
     <header>
        <img>
     </header>
        <main>
      <player></player>
      <section>
      <nav>
          <router-link to='/search'>搜索</router-link>
          <router-link to='/hot'>热榜</router-link> 
          <router-link to='/list'>新歌</router-link> 
          <router-link to='/history'>历史</router-link>    
      </nav>
      <keep-alive >
          <router-view ></router-view>
        </keep-alive> 
      </section>
     </main>
      <footer>
        <BottomBtn></BottomBtn>
      </footer>
      <About></About>
  </div>
</template>

数据流

有空再画

踩的坑

手机chrome100vh多了导航栏的高度

监听window.resize事件,动态计算高度

具体的看这里

移动端audio元素填充了src不会自动播放

填充了数据后加个触发

document.querySelector('#player').play()

性能问题

动画用多了,姑且去掉了些效果(模糊之类的),用了玄学的will-changetransform: translate3d(0, 0, 0),不过感觉好像没啥用,看来得用transform替代一些高宽大小变换

其他想起来再补充

不足

我已经不管兼容性了啊哈哈哈哈哈哈

滥用flex布局;

Polyfill?那是什么?

还是用了jquery

还只用了jq的ajax,因为用axios遇到了些问题。

后续计划

播放模式

歌词及其相关

桌面端布局

收藏列表

本地存储

涟漪效果

ps:谁知道什么时候去做呢,啊哈哈哈哈

以上!

2B小姐姐~

从一滴水说起,谈谈CSS形状的生成思路

水是生命之源、生产之要、生态之基。兴水利、除水害,事关人类生存、社会进步,历来是治国安邦的大事。巴拉巴拉~不扯淡了,

来看看下面这张图,额,为了扣题,就管她叫水滴吧(虽然是倒的),从这开始,让我们用css来生成她~

fN.png

1.首先把她理解成一个圆与一个三角组合而成,这样,就有了第一种组合法

    .box1 {
        width: 100px;
        height: 100px;
        background-color: red;
        border-radius: 50%;
        position: relative;
    }

    .box1::after {
        position: absolute;
        content: '';
        width: 50%;
        height: 50%;
        background-color: #000;
        /*     transform: rotate(45deg) translate(-50%, -50%);
        left: 50%;
        top: 50%;*/
        transform: rotate(45deg);
        left: 25%;
        top: 60%;
    }

简单粗暴,一个圆加一个旋转的方块露出的三角,在这里还尝试了下用translate来尝试定位,虽然失败了(不好定位,没旋转的时候很好用,且能使用基于自身的百分比值来定位,可方便的实现垂直居中)。

2.还有种思路,一个竖着的椭圆,把她的左下右下想把法去掉,然后就有了以下两种尝试

    .box2 {
        width: 100px;
        height: 100px;
        background-color: red;
        border-radius: 50%;
        position: relative;
    }
    .box2::after {
        position: absolute;
        content: '';
        width: 100%;
        height: 100%;
        background-color: red;
        background: linear-gradient(-45deg,#fff  67%, transparent 0) right, linear-gradient(45deg, #fff 67%, transparent 0) left;  background-size: 50% 100%;
        background-repeat: no-repeat;
        top: 50%;
    }

再这里抱个歉,最终效果我没调成,果然一边看直播一边写demo就是没效率,话说96B跑的真块啊

先拿一个圆,再拿一个豁了一个三角的长方形给挡住,差不多是这意思~

    .box3 {
        width: 100px;
        height: 100px;
        background: linear-gradient(-45deg, transparent 33%, red 0) right, linear-gradient(45deg, transparent 33%, red 0) left;
        background-size: 50% 100%;
        background-repeat: no-repeat;
        border-radius: 50% 50%;
        position: relative;
    }

这个是上面的改进版,直接对自身使用径向渐变 ,把左右两角设为透明,最终效果也没调成,囧

3.顺着上面的思路,自然想到了能直接对圆进行切割的clip-path属性虽然兼容堪忧

    .box4 {
        width: 100px;
        height: 100px;
        position: relative;
        background-color: red;
        -webkit-clip-path: inset(0 0 0 0 round 50% 50% 0 50%);
        -o-clip-path: inset(0 0 0 0 round 50% 50% 0 50%);
        clip-path: inset(0 0 0 0 round 50% 50% 0 50%);
        transform: rotate(45deg);
    }

照理说直接切割应该能切出来,但我没弄出来,都是直播的锅,这里我取了个巧,弄了个三个角取圆,一个角直角,最后旋转的方法。

http://bennettfeely.com/clippy/   附个clip-path生成器,虽然不能生成本文的水滴。

4.其实都能想到了,3里面为什么不直接用border-radius生成圆角呢,兼容还好点,于是

    .box1 {
        width: 100px;
        height: 100px;
        background-color: red;
        border-radius: 50% 50% 0; /*top;leftright;bottom*/
        transform: rotate(45deg);
   }

我也不是谦虚,其实我第一个想出来的方法就是这个,上面的就是想凑点字数,代码简洁,易于理解,我网上找了下好像也没人写锅这个,好顶赞~

总结:

想要一个形状,我们可以遮,切,组合,旋转,这么多种方法,结合伪元素、动画属性,真是其乐无穷~

当然了可以直接base64或者用图片

http://pan.baidu.com/s/1dFlxtGP 上面demo放这了,顺序不对请不要介意,话说有空得搞个demo页了。

附:CSS生成云朵

    .demo {
        height: 50px;
        width: 50px;
        box-shadow: #eee 65px -15px 0 -5px, #eee 25px -25px, #eee 30px 10px, #eee 60px 15px 0 -10px, #eee 85px 5px 0 -5px;
        border-radius: 50%;
    }

自身是圆,生成自身的阴影再偏移,许多个阴影组合而出的云。

可以在http://www.vastskycc.com/404.html这里看到,啊,当然不是我写的,哭~

fce67b41d30cedcc6f4756395a4306f4.jpg

《javascript高级程序设计》笔记1(第2章 在HTML中使用JS,第3章 基本概念)

犀牛书和红皮书以前是读完了,不过一来是没经验,二来是读的太粗。工作了之后,也只是浅浅的用下JQ,唔,就是一个简单却带点无聊的切图仔。
不过呢,自己好赖也是个小年轻,按捺不住的想要接触些酷炫的东西(react之类的),然后就认识到自己薄弱的JS基础了,于是呢,我就重拾起吃灰已久的红皮书,细细品味就是写点笔记,,,再摘录一点放到网上。
那么,开始了~

代码示例:https://github.com/ccchangkong/js/tree/master/js

第一章介绍略过。

第二章在HTML中使用JS。

在<script>中有6个属性,值得一提的是defer延迟加载属性。

第三章基本概念

数值:数值转换的3个函数Number()、parseInt()、parseFloat()。parseInt()可传第二个参数设置进制。

位操作:数值以64位格式存储、32位格式进行位操作。负数为补码表示(取反码再+1)。~非、&与、|或、^异或(不同出一)。<<、>>无符号(正负)移位数、<<<、>>>有符号移位数。

布尔操作符:!取反、&&与(有null出null,null是没有值;有underfined出underfined,underfined为未定义值;有NaN出NaN,NaN为不是数值)、||或。

乘性操作符:*乘、/除、%取模。

加性操作符:+(字符拼接主意数字,可用圆括号先操作)、-。

关系操作符:<、>、<=、>=。例B<a为true,都为字符串时比较字符编码值,大写总小于小写;“23”<“3”为true;“23”<3为false;“a”<“3”为false,a转换为NaN。

相等操作符:==、!=、===、!==(带比较类型)。NaN!=NaN,null==underfined。

label与break配合跳出循环嵌套。

函数:arguements[].

IMG_20160709_214141.jpg

萌萌哒小埋书签~

新的开始

c40.jpg

5月6号答辩,5月9号开始工作。

思绪万千,却不知道该怎么表达出来。

很突然,自己的学生时代就结束了,匆匆四年,虽然没留下多少值得纪念的事情,但认识了很多有意思的人,自己也有点小小的努力,过的不算多后悔。这几年,自己也有做的不对的地方,今后也会以之为鉴。

我是个自以为很随性、有点正义感的普通人,很庆幸没做过什么违背自己三观的事,很幸运。

往事不多提,是结束,也是新的开始。

《javascript高级程序设计》笔记2(第4章 变量、作用域和内存问题,第5章 引用类型)

代码示例:https://github.com/ccchangkong/js/tree/master/js

第四章 变量、作用域和内存问题

4.1变量

引用类型:可添加属性,如Object,存放在堆内存,变量为指针;

基本类型:Undefined、Null、Boolean、Number、String、~栈内存;

var obj1=new Object();

var obj2=obj1;

obj1.name="N";

alert(obj2.name);//"N";

var num1=5;

var num2=num1;

num1=10:

alert(num2)//5;

对象传参是按值传递,而非按引用传递;

typeof:检验基本类型的最佳方法;

result=variable instanceof constructor 判断引用类型(Object,Array,RegExp);

4.2作用域

作用域链从底向上;

IE8-,在外部可访问catch语句内的变量;

js没有块级作用域(if,for);

4.3垃圾收集

设null解除引用;

第五章 引用类型

5.1 Object类型

创建实例 

var person=new Object();//构造函数

var person={};//对象字面量表示法

访问对象属性
person["name"];

person.name;

5.2 Array类型

构造函数创建可省略new;

数组字面量表示法(IE8下游bug);

length

设置length可从末尾移项或添加新项;

方便的添加新项 a[a.length];

检测数组

ES3 value instanceof Array

ES5 Array.isArray(value)

转换

toString() 以逗号分隔的字符串

valueOf() 返回数组

alert() 后台会调用toString() 

join() 可使用不同的分隔符来构建字符串

栈方法

先进后出

push() 推入到最后并返回修改后的长度

pop() 移除最后并返回该值

队列方法

先进先出

shift() 移除第一项并返回该值

unshift() 在前端添加并返回修改后的长度

重排序方法

reverse() 反转顺序

sort() 按升序排列数组,比较的是字符串,“5”>“10”

function compare(v1,v2){

    if(v1<v2){

        return -1;

    }else if(v1>v2){

        return 1;        

    }else{

        return 0;        

    }

}

vs.sort(compare);

function compaer(v1,v2){

    return v2-v1;

}  //数值类型 

操作方法

contant() 复制当前数组并返回新数组,如有传值则添置新数组末尾

slice() 接收1或2个参数(返回项的起始和结束,负数为长度+负数)

splice() 删除 2个参数 (起始位置,删除项数);插入 3个参数 (起始位置,删除项数0,插入项);替换3个参数 (起始位置,删除项数,替换项)

位置方法

indexOf() 参数为要查找的项和起点位置索引(可选),没找到返回-1,比较用的===

lastIndexOf() 倒序

迭代方法(ES5 IE9+)

every() 对每一项运行给定函数,全为true则返回true

filter() 对每一项运行给定函数,返回结果为true的项构成的数组

forEach() 对每一项运行给定函数,无返回

map() 对每一项运行给定函数,返回结果构成的数组

some() 对每一项运行给定函数,只要有true则返回true

function(item,index,array){};

归并方法(ES5 IE9+)

迭代数组的所有项,然后构建一个最终返回的值

reduce()

reduceRight()

function(prev,cur,index,array){};

5.3Date类型

UTC时间 (1970.1.1 00:00开始的毫秒数)

Date.parse("6/13/2004");返回UTC毫秒数,支持多种日期格式

Date.UTC(2005,4,5,17,55,55);//GMT 2005.5.5 17:55:55 日默认是1,其余默认0

ES5 Date.now() 等同于 + new Date();

继承的方法

    value0f()返回毫秒数

日期格式化方法

    推荐toUTCString()

日期/时间组件方法

    太多了。。

5.4 RegExp类型

var expression=/pattern/flags;

new RegExp["pattern","flags"];

pattern 正则表达式;flags标志(g全局,i不区分大小写,m多行)

元字符(需转义,加转义符号\) ([{^$|)?*+.]}

字面量方式共享同一实例,而构造函数创建的实例每个都是新的;

RegExp实例方法

exec() 接受要应用的字符串,返回包含第一个匹配项信息的数组或null,为Array实例,多了index和input属性

test() 返回布尔值

5.5 Function 类型

函数名是一个指向函数对象的指针

没有重载

    变量被覆盖了

作为值的函数

    去掉函数名后的括号可访问函数的指针而不执行函数

函数内部属性

    arguments.callee 指向arguments对象的函数,可用于解耦

    this 函数据以执行的环境对象

    caller(ES5 Opera9.6+) 保存着调用当前函数的函数引用

函数属性和方法

    length 函数希望接收的命名参数的个数

    prototype 第六章

    apply() call() 在特定的作用域中调用函数

    apply() 两个参数,作用域和参数数组

    call() 作用域,其余参赛需逐个列举出来

    bind() IE9+ 会创建一个函数的实例,其this值会被绑定到传给bind()函数的值

    window.color="red";

    var o={color:"blue"};

    function sayColor(){

        alert(this.color);

    }

    var os =sayColor.bind(o);

    os();//blue

5.6基本包装类型

Boolean,Number,String

    是基本类型值,但封装了一些方法,因为对象的生存期,不能再运行时为基本类型值添加属性和方法

Boolean

    略

Number

    toString() 以n进制数值的字符串形式返回

    toFixed()    返回指定的小数位数值的字符串

    toExponential()    指数表示法..

    toPrecision()    以合适的形式..

String

    length() 包含多少个字符

    charAt()    返回给定位置的那个字符

    charCodeAt()    。。。的编码

    (ES5 IE8+)stringValue[1]

    contact() 拼接字符并将其返回为新字符串(不如用+)

    slice(),substr(),substring() 返回心字符串

    indexOf 返回从头开始的子字符串位置,没有则返回-1,可接受第二个参数,表示初始位置

    lastIndexOf() ..尾。。

    trim() (ES5,IE9+)去前后空格

    match(),search() 匹配正则

    replace() 两个参数,正则表达式或字符串(只替换第一个),字符串或函数(模式匹配项,匹配到的位置,原始字符串)

    split() 指定分隔符将字符串切割并返回一个数组,可指定第2个参数(数组的最大长度)

    localCompare() 比较,返回1或-1或0

5.7单体内置对象

Global对象

    encodeURL(),encodeURLCompontent() URI编码,后者用的多

    decodeURL(),decodeURLCompontent()  解码

    eval() 解析器,注意使用安全

window 第八章

Math

    Math.E e

    Math.LN10 以e为底10的自然对数

    Math.PI π

    min(),max()确定最小值和最大值,数组中:var vs=[];var max=Math.max.apply(Math,vs);

    ceil() 向上舍入

    floor() 向下舍入

    round() 四舍五入

    random() 返回0到1的随机数

    Math.abs(num) 返回num的绝对值,还好多不写了。

 

6f4ec6f41bd5ad6edb6415f389cb39dbb7fd3cdb.jpg

字好多。。

复杂插件系列

#复杂插件系列

帮助后台开发管理界面时用到了一系列比较复杂的前端插件(主要为datatable.js、layer.js、webuploader.js、ueditor.js),以及在这基础上实现了一些功能,在此记录。

##datatable.js

###封装文件

$.fn.dataTable.ext.errMode = function (e, settings, techNote, message) {
    layer.msg('表格加载错误!', { icon: 5 });//使用layer弹窗插件统一显示错误
}

var Common = {
    GetQueryString: function (name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
        var r = window.location.search.substr(1).match(reg);
        if (r != null) return unescape(r[2]); return null;
    },
    StatusColor: function (status, desc) {
        var result = "";
        switch (status) {
            case 0:
                result = "<span style='color:green'>" + desc + "</span>";
                break;
            case 1:
                result = "<span style='color:black'>" + desc + "</span>";
                break;
            case 2:
                result = "<span style='color:gray'>" + desc + "</span>";
                break;
            case 3:
                result = "<span style='color:red'>" + desc + "</span>";
                break;
            default:
                result = desc;
                break;
        }
        return result;
    },
    Language: {
        "sProcessing": "正在获取数据,请稍后...",
        "sInfo": "&nbsp;&nbsp;当前显示_START_-_END_&nbsp;_PAGE_/_PAGES_&nbsp;共_TOTAL_条",
        "sLengthMenu": "_MENU_",
        "sEmptyTable": "未获取到数据", //"当前显示1-10 x/y 共41807条"
        "sInfoEmpty": "",
        "oPaginate": {
            "sFirst": "首页",
            "sLast": "尾页",
            "sNext": "下一页",
            "sPrevious": "上一页",
            "sSkip": "跳转"
        },
        metronicGroupActions: "_TOTAL_ records selected:  "
    },
    transformOrder: function (d, tbgrid) {
        if (d.order && d.order.length > 0) {
            if (tbgrid.getDataTable() != undefined) {
                var columnNames = tbgrid.getDataTable().columns().dataSrc();
                if (columnNames && columnNames.length > 0) {
                    for (var i in d.order) {
                        d.order[i].column = columnNames[d.order[i].column];
                    }
                }
            } else {
                d.order = [];
            }
        }
        return d;
    }
};

//服务器分页配置
var Datatable = function () {
    var tableOptions;
    var dataTable;
    var table;
    var the;
    var selectedRow;
    return {
        init: function (options) {
            if (!$().dataTable) {
                return;
            }
            the = this;
            options.src = $("#" + options.src);
            options = $.extend(true, {
                src: "",
                filterApplyAction: "filter",
                filterCancelAction: "filter_cancel",
                resetGroupActionInputOnSuccess: true,
                loadingMessage: 'Loading...',
                dataTable: {
                    "dom": "t<'row'<'col-md-5 col-sm-5'li><'col-md-7 col-sm-7'p>>",
                    "pageLength": 15, // 默认条数
                    "bLengthChange": true, //改变每页显示数据数量
                    "lengthMenu": [[5, 10, 15, 25, 50], [5, 10, 15, 25, 50]],
                    "oLanguage": Common.Language, //分页语言
                    "bProcessing": true, //DataTables载入数据时,是否显示‘进度’提示  
                    "orderCellsTop": true,
                    "bPaginate": true, //是否分页
                    "pagingType": "full_numbers", //详细分页组

                    "bFilter": false, //是否启动过滤、搜索功能  
                    "autoWidth": false, // 启用或禁用自动列宽度的计算 , 关闭后,表格将不会自动计算表格大小,在浏览器大化小化的时候会挤在一坨

                    "processing": true, // 开启读取服务器数据
                    "serverSide": true, // 开启服务器分页模式
                    "bSort": true,//是否启动各个字段的排序功能

                    "bAutoWidth": false, //是否自适应宽度  
                    "columnDefs": [{ // 列属性

                        'bSortable': false,
                        'targets': [0]
                    }],

                    "ajax": {
                        "url": "",
                        "type": "POST",
                        "data": function (d) {
                            return { "param": JSON.stringify(d) }
                        },
                        "dataSrc": function (res) { // 处理从服务器返回数据                           
                            if (tableOptions.onSuccess) {
                                tableOptions.onSuccess.call(undefined, the, res);
                            }
                            return res.data;
                        },
                        "error": function (XMLHttpRequest, textStatus, errorThrown) { // 处理一般错误

                            //重新登录
                            if (XMLHttpRequest != null || XMLHttpRequest != undefined) {
                                var result = "";
                                if (typeof (XMLHttpRequest) == "object") {
                                    result = JSON.stringify(XMLHttpRequest);
                                }
                                else {
                                    result = XMLHttpRequest.toString();
                                }
                                //if (result.indexOf("Content/Css/Shared/img/relogin_03.png") != -1) {
                                //    layer.closeAll();
                                //    $("#rightContent").load("/Account/ReLogin");
                                //}
                            }

                            if (tableOptions.onError) {
                                tableOptions.onError.call(undefined, the);
                            }
                        }
                    },
                    "drawCallback": function (oSettings) { // 表格重绘执行
                        $('input.flat').iCheck({
                            checkboxClass: 'icheckbox_flat-green',
                            radioClass: 'iradio_flat-green'
                        });
                    }
                }
            }, options);
            tableOptions = options;
            table = $(options.src);
            dataTable = table.DataTable(options.dataTable);
            return dataTable;
        },

        submitFilter: function () {
            the.setAjaxParam("action", tableOptions.filterApplyAction);

            $('textarea.form-filter, select.form-filter, input.form-filter:not([type="radio"],[type="checkbox"])', table).each(function () {
                the.setAjaxParam($(this).attr("name"), $(this).val());
            });

            $('input.form-filter[type="checkbox"]:checked', table).each(function () {
                the.addAjaxParam($(this).attr("name"), $(this).val());
            });

            $('input.form-filter[type="radio"]:checked', table).each(function () {
                the.setAjaxParam($(this).attr("name"), $(this).val());
            });

            dataTable.ajax.reload();
        },

        resetFilter: function () {
            $('textarea.form-filter, select.form-filter, input.form-filter', table).each(function () {
                $(this).val("");
            });
            $('input.form-filter[type="checkbox"]', table).each(function () {
                $(this).attr("checked", false);
            });
            the.clearAjaxParams();
            the.addAjaxParam("action", tableOptions.filterCancelAction);
            dataTable.ajax.reload();
        },

        getSelectedRowsCount: function () {
            return $('tbody > tr > td:nth-child(1) input[type="checkbox"]:checked', table).size();
        },

        getSelectedRow: function () {
            var id = null;
            if (selectedRow) {
                id = $("td:first-child input[type=checkbox]", selectedRow).attr("data-id");
            }
            return id || -1;

        },
        getDataTable: function () {
            return dataTable;
        },
        getTable: function () {
            return table;
        }


    };

};

//前端异步数据分页
var DatatableCC = function () {
    var tableOptions;
    var dataTable;
    var table;
    var the;
    var selectedRow;
    return {
        init: function (options) {
            if (!$().dataTable) {
                return;
            }
            the = this;
            options.src = $("#" + options.src);
            options = $.extend(true, {
                src: "",
                filterApplyAction: "filter",
                filterCancelAction: "filter_cancel",
                resetGroupActionInputOnSuccess: true,
                //loadingMessage: 'Loading...',
                responsive: true,
                dataTable: {
                    //"dom": "t<'row'<'col-md-5 col-sm-5'li><'col-md-7 col-sm-7'p>>",
                    "dom": "t<'_center'<'col-md-5 col-sm-5'><'col-md-7 col-sm-7 tdPage tdColor'p>>", //表格样式
                    "pageLength": 15, // 默认条数
                    "bLengthChange": true, //改变每页显示数据数量
                    "lengthMenu": [[5, 10, 15, 25, 50], [5, 10, 15, 25, 50]],
                    "oLanguage": Common.Language, //分页语言
                    "bProcessing": false, //DataTables载入数据时,是否显示‘进度’提示  
                    "orderCellsTop": true,
                    "bPaginate": true, //是否分页
                    "pagingType": "full_numbers", //详细分页组
                    "bFilter": false, //是否启动过滤、搜索功能  
                    "bSort": true,//是否启动各个字段的排序功能
                    "autoWidth": false,
                    "bAutoWidth": false,
                    "sPaginationType": "bootstrap",
                    //"bProcessing": true,    //显示处理状态
                    "drawCallback": function (settings) {
                        $('input.flat').iCheck({
                            checkboxClass: 'icheckbox_flat-green',
                            radioClass: 'iradio_flat-green'
                        });
                    }
                }
            }, options);
            tableOptions = options;
            table = $(options.src);
            dataTable = table.DataTable(options.dataTable);
            return dataTable;
        },
        getDataTable: function () {
            return dataTable;
        },
        getTable: function () {
            return table;
        }


    };

};

###调用

//调用

//服务器分页
//实例化
 var grid = new Datatable();
 //初始化
grid.init({
    src: "datatable", //表格ID
    dataTable: {
        "sPaginationType": "bootstrap",
        "bProcessing": true, //显示处理状态
        "dom": "t<'_center'<'col-md-5 col-sm-5'><'col-md-7 col-sm-7 tdPage tdColor'p>>", //表格样式

        "aoColumns": [{

                data: 'Product_Name',
                title: '产品名称'

            }
            {
                title: '操作',
                render: function(data, type, full) {
                    data = '<div style="margin:auto;display: table; " class="handle" data-id="' + full.Product_ID + '">';
                        data += '<button class="btn btn-primary btn-xs  more"><i class="fa fa-table"></i> 详细</button>\
            <button class="btn btn-info  btn-xs do"><i class="fa fa-pencil"></i> 修改</button>\
            <button class="btn btn-danger btn-xs  del"><i class="fa fa-trash-o"></i> 删除</button>';
                    data += '</div>';
                    return data
                },
            }

        ],
        "ajax": {
            "url": "",
            "type": "post",
            "data": function(d) {
                d = Common.transformOrder(d, grid);
                d.condition = [{
                        "name": "CategoryID",
                        "value": $(".manageUserTop [name='CategoryTypeAll']").val()
                    },
                    {
                        "name": "Product_ID",
                        "value": $(".manageUserTop [name='CategoryAll']").val()
                    }
                ];
                return {
                    "param": JSON.stringify(d)
                }
            },
            "dataSrc": function(res) { // 处理从服务器返回数据
                return res.data;
            }
        }
    }
});
//刷新
grid.getDataTable().ajax.reload()

//前端分页
//实例化
var grid = new DatatableCC();
 //初始化
grid.init({
    src: "datatable", //表格ID
    dataTable: {
        //"pageLength": 2,    //每页显示的记录数
        "sPaginationType": "bootstrap",
        "bProcessing": true, //显示处理状态

        "dom": "t<'_center'<'col-md-5 col-sm-5'><'col-md-7 col-sm-7 tdPage tdColor'p>>", //表格样式
        "aoColumns": [

            {

                data: 'OperationTag',
                title: '操作权限标签'

            }
            {
                title: '操作',
                render: function(data, type, full) {
                    data = '<div style="margin:auto;display: table; " class="handle" data-operationtag="' + full.OperationTag + '" data-id="' + full.Id + '">';
                    data += '<button class="btn btn-info  btn-xs do" ><i class="fa fa-pencil"></i> 修改</button>\
                            <button class="btn btn-danger btn-xs  del" ><i class="fa fa-trash-o"></i> 删除</button>';
                    data += '</div>';
                    return data
                },
            }

        ],
        "ajax": {
            "url": "",
            "type": "post",
            "data": {
                "operationTag": function() {
                    return $('#operationTag').val()
                }
            },
            "dataSrc": function(res) { // 处理从服务器返回数据
                if(res.IsSuccess) {
                    return res.Model;
                }
            }
        }
    }
});
//刷新
grid.getDataTable().ajax.reload()

###说明
以后补充

##layer.js
这里提layer这个插件是因为与bootstrap模态框z-index层级的问题,解决方法是重置bootstrap模态框的层级

.modal-backdrop {
    z-index: 10;
}

.modal {
    z-index: 41;
}

##webuploader.js

###封装文件

// 当domReady的时候开始初始化
var Uploader = function () {
    var uploader;
    var the;
    return {
        init: function ($wrap, option) {
            // var $wrap = $('#uploader'),
            the = this;
            // 图片容器
            var $queue = $('<ul class="filelist"></ul>')
                  .appendTo($wrap.find('.queueList')),

              // 状态栏,包括进度和控制按钮
                  $statusBar = $wrap.find('.statusBar'),

              // 文件总体选择信息。
                  $info = $statusBar.find('.info'),

              // 上传按钮
                  $upload = $wrap.find('.uploadBtn'),

              // 没选择文件之前的内容。
                  $placeHolder = $wrap.find('.placeholder'),

                  $progress = $statusBar.find('.progress').hide(),

              // 添加的文件数量
                  fileCount = 0,

              // 添加的文件总大小
                  fileSize = 0,

              // 优化retina, 在retina下这个值是2
                  ratio = window.devicePixelRatio || 1,

              // 缩略图大小
                  thumbnailWidth = 110 * ratio,
                  thumbnailHeight = 110 * ratio,

              // 可能有pedding, ready, uploading, confirm, done.
                  state = 'pedding',

              // 所有文件的进度信息,key为file id
                  percentages = {},
              // 判断浏览器是否支持图片的base64
                  isSupportBase64 = (function () {
                      var data = new Image();
                      var support = true;
                      data.onload = data.onerror = function () {
                          if (this.width != 1 || this.height != 1) {
                              support = false;
                          }
                      }
                      data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
                      return support;
                  })(),

              // 检测是否已经安装flash,检测flash的版本
                  flashVersion = (function () {
                      var version;

                      try {
                          version = navigator.plugins['Shockwave Flash'];
                          version = version.description;
                      } catch (ex) {
                          try {
                              version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')
                                  .GetVariable('$version');
                          } catch (ex2) {
                              version = '0.0';
                          }
                      }
                      version = version.match(/\d+/g);
                      return parseFloat(version[0] + '.' + version[1], 10);
                  })(),

                  supportTransition = (function () {
                      var s = document.createElement('p').style,
                          r = 'transition' in s ||
                          'WebkitTransition' in s ||
                          'MozTransition' in s ||
                          'msTransition' in s ||
                          'OTransition' in s;
                      s = null;
                      return r;
                  })();

            // WebUploader实例


            if (!WebUploader.Uploader.support('flash') && WebUploader.browser.ie) {

                // flash 安装了但是版本过低。
                if (flashVersion) {
                    (function (container) {
                        window['expressinstallcallback'] = function (state) {
                            switch (state) {
                                case 'Download.Cancelled':
                                    layer.msg('您取消了更新!')
                                    break;

                                case 'Download.Failed':
                                    layer.msg('安装失败')
                                    break;

                                default:
                                    layer.msg('安装已成功,请刷新!');
                                    break;
                            }
                            delete window['expressinstallcallback'];
                        };

                        var swf = './expressInstall.swf';
                        // insert flash object
                        var html = '<object type="application/' +
                            'x-shockwave-flash" data="' + swf + '" ';

                        if (WebUploader.browser.ie) {
                            html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
                        }

                        html += 'width="100%" height="100%" style="outline:0">' +
                            '<param name="movie" value="' + swf + '" />' +
                            '<param name="wmode" value="transparent" />' +
                            '<param name="allowscriptaccess" value="always" />' +
                            '</object>';

                        container.html(html);

                    })($wrap);

                    // 压根就没有安转。
                } else {
                    $wrap.html('<a href="http://www.adobe.com/go/getflashplayer" target="_blank" border="0"><img alt="get flash player" src="http://www.adobe.com/macromedia/style_guide/images/160x41_Get_Flash_Player.jpg" /></a>');
                }

                return;
            } else if (!WebUploader.Uploader.support()) {
                layer.msg('Web Uploader 不支持您的浏览器!');
                return;
            }

            // 实例化
            uploader = WebUploader.create(option);

            // 拖拽时不接受 js, txt 文件。
            //uploader.on('dndAccept', function (items) {
            //    var denied = false,
            //        len = items.length,
            //        i = 0,
            //        // 修改js类型
            //        unAllowed = 'text/plain;application/javascript ';

            //    for (; i < len; i++) {
            //        // 如果在列表里面
            //        if (~unAllowed.indexOf(items[i].type)) {
            //            denied = true;
            //            break;
            //        }
            //    }

            //    return !denied;
            //});

            // uploader.on('filesQueued', function() {
            //     uploader.sort(function( a, b ) {
            //         if ( a.name < b.name )
            //           return -1;
            //         if ( a.name > b.name )
            //           return 1;
            //         return 0;
            //     });
            // });

            // 添加“添加文件”的按钮,
            uploader.addButton({
                id: option.pick2,
                label: '继续添加'
            });

            uploader.on('ready', function () {
                window.uploader = uploader;
            });
            //成功回调
            //uploader.on('uploadSuccess', function (file, response) {
            //    uploader.removeFile(file);
            //    //var imgurl = response.url;    //上传图片路径
            //    //$('#images').val(imgurl);
            //    //$('#' + file.id).addClass('upload-state-success').find(".state").text("已上传");
            //});

            // 当有文件添加进来时执行,负责view的创建
            function addFile(file) {
                var $li = $('<li id="' + file.id + '">' +
                        '<p class="title">' + file.name + '</p>' +
                        '<p class="imgWrap"></p>' +
                        '<p class="progress"><span></span></p>' +
                        '</li>'),

                    $btns = $('<div class="file-panel">' +
                        '<span class="cancel">删除</span>' +
                        '<span class="rotateRight">向右旋转</span>' +
                        '<span class="rotateLeft">向左旋转</span></div>').appendTo($li),
                    $prgress = $li.find('p.progress span'),
                    $wrap = $li.find('p.imgWrap'),
                    $info = $('<p class="error"></p>'),

                    showError = function (code) {
                        switch (code) {
                            case 'exceed_size':
                                text = '文件大小超出';
                                break;

                            case 'interrupt':
                                text = '上传暂停';
                                break;

                            default:
                                text = '上传失败,请重试';
                                break;
                        }

                        $info.text(text).appendTo($li);
                    };

                if (file.getStatus() === 'invalid') {
                    showError(file.statusText);
                } else {
                    // @todo lazyload
                    $wrap.text('预览中');
                    uploader.makeThumb(file, function (error, src) {
                        var img;

                        if (error) {
                            $wrap.text('不能预览');
                            return;
                        }

                        if (isSupportBase64) {
                            img = $('<img src="' + src + '">');
                            $wrap.empty().append(img);
                        } else {
                            //$.ajax('../../server/preview.php', {
                            //    method: 'POST',
                            //    data: src,
                            //    dataType: 'json'
                            //}).done(function (response) {
                            //    if (response.result) {
                            //        img = $('<img src="' + response.result + '">');
                            //        $wrap.empty().append(img);
                            //    } else {
                            //        $wrap.text("预览出错");
                            //    }
                            //});
                            $wrap.text("预览出错");
                        }
                    }, thumbnailWidth, thumbnailHeight);

                    percentages[file.id] = [file.size, 0];
                    file.rotation = 0;
                }

                file.on('statuschange', function (cur, prev) {
                    if (prev === 'progress') {
                        $prgress.hide().width(0);
                    } else if (prev === 'queued') {
                        $li.off('mouseenter mouseleave');
                        $btns.remove();
                    }

                    // 成功
                    if (cur === 'error' || cur === 'invalid') {
                        console.log(file.statusText);
                        showError(file.statusText);
                        percentages[file.id][1] = 1;
                    } else if (cur === 'interrupt') {
                        showError('interrupt');
                    } else if (cur === 'queued') {
                        percentages[file.id][1] = 0;
                    } else if (cur === 'progress') {
                        $info.remove();
                        $prgress.css('display', 'block');
                    } else if (cur === 'complete') {
                        $li.append('<span class="success"></span>');
                    }

                    $li.removeClass('state-' + prev).addClass('state-' + cur);
                });

                $li.on('mouseenter', function () {
                    $btns.stop().animate({ height: 30 });
                });

                $li.on('mouseleave', function () {
                    $btns.stop().animate({ height: 0 });
                });

                $btns.on('click', 'span', function () {
                    var index = $(this).index(),
                        deg;

                    switch (index) {
                        case 0:
                            uploader.removeFile(file);
                            return;

                        case 1:
                            file.rotation += 90;
                            break;

                        case 2:
                            file.rotation -= 90;
                            break;
                    }

                    if (supportTransition) {
                        deg = 'rotate(' + file.rotation + 'deg)';
                        $wrap.css({
                            '-webkit-transform': deg,
                            '-mos-transform': deg,
                            '-o-transform': deg,
                            'transform': deg
                        });
                    } else {
                        $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')');
                        // use jquery animate to rotation
                        // $({
                        //     rotation: rotation
                        // }).animate({
                        //     rotation: file.rotation
                        // }, {
                        //     easing: 'linear',
                        //     step: function( now ) {
                        //         now = now * Math.PI / 180;

                        //         var cos = Math.cos( now ),
                        //             sin = Math.sin( now );

                        //         $wrap.css( 'filter', "progid:DXImageTransform.Microsoft.Matrix(M11=" + cos + ",M12=" + (-sin) + ",M21=" + sin + ",M22=" + cos + ",SizingMethod='auto expand')");
                        //     }
                        // });
                    }


                });

                $li.appendTo($queue);
            }

            // 负责view的销毁
            function removeFile(file) {
                var $li = $('#' + file.id);

                delete percentages[file.id];
                updateTotalProgress();
                $li.off().find('.file-panel').off().end().remove();
            }

            function updateTotalProgress() {
                var loaded = 0,
                    total = 0,
                    spans = $progress.children(),
                    percent;

                $.each(percentages, function (k, v) {
                    total += v[0];
                    loaded += v[0] * v[1];
                });

                percent = total ? loaded / total : 0;


                spans.eq(0).text(Math.round(percent * 100) + '%');
                spans.eq(1).css('width', Math.round(percent * 100) + '%');
                updateStatus();
            }

            function updateStatus() {
                var text = '',
                    stats;

                if (state === 'ready') {
                    text = '选中' + fileCount + '个文件,共' +
                        WebUploader.formatSize(fileSize) + '。';
                } else if (state === 'confirm') {
                    stats = uploader.getStats();
                    if (stats.uploadFailNum) {
                        text = '已成功上传' + stats.successNum + '个文件至服务器,' +
                            stats.uploadFailNum + '个文件上传失败,<a class="retry" href="#">重新上传</a>失败文件'
                    }

                } else {
                    stats = uploader.getStats();
                    text = '共' + fileCount + '个(' +
                        WebUploader.formatSize(fileSize) +
                        '),已上传' + stats.successNum + '个';

                    if (stats.uploadFailNum) {
                        text += ',失败' + stats.uploadFailNum + '个';
                    }
                }

                $info.html(text);
            }

            function setState(val) {
                var file, stats;

                if (val === state) {
                    return;
                }

                $upload.removeClass('state-' + state);
                $upload.addClass('state-' + val);
                state = val;

                switch (state) {
                    case 'pedding':
                        $placeHolder.removeClass('element-invisible');
                        $queue.hide();
                        $statusBar.addClass('element-invisible');
                        uploader.refresh();
                        break;

                    case 'ready':
                        $placeHolder.addClass('element-invisible');
                        $(option.pick2).removeClass('element-invisible');
                        $queue.show();
                        $statusBar.removeClass('element-invisible');
                        uploader.refresh();
                        break;

                    case 'uploading':
                        $(option.pick2).addClass('element-invisible');
                        $progress.show();
                        $upload.text('暂停上传');
                        break;

                    case 'paused':
                        $progress.show();
                        $upload.text('继续上传');
                        break;

                    case 'confirm':
                        $progress.hide();
                        $(option.pick2).removeClass('element-invisible');
                        $upload.text('开始上传');

                        stats = uploader.getStats();
                        if (stats.successNum && !stats.uploadFailNum) {
                            setState('finish');
                            return;
                        }
                        break;
                    case 'finish':
                        stats = uploader.getStats();
                        if (stats.successNum) {
                            //uploader.reset();
                            layer.msg('上传成功');
                            if (option.jump) {
                                setTimeout(function () {
                                    toUrl(option.jump)
                                }, 500)

                            }
                        } else {
                            // 没有成功的图片,重设
                            state = 'done';
                            location.reload();
                        }
                        break;
                }

                updateStatus();
            }



            uploader.onUploadProgress = function (file, percentage) {
                var $li = $('#' + file.id),
                    $percent = $li.find('.progress span');

                $percent.css('width', percentage * 100 + '%');
                percentages[file.id][1] = percentage;
                updateTotalProgress();
            };

            uploader.onFileQueued = function (file) {
                fileCount++;
                fileSize += file.size;

                if (fileCount === 1) {
                    $placeHolder.addClass('element-invisible');
                    $statusBar.show();
                }

                addFile(file);
                setState('ready');
                updateTotalProgress();
            };

            uploader.onFileDequeued = function (file) {
                fileCount--;
                fileSize -= file.size;

                if (!fileCount) {
                    setState('pedding');
                }

                removeFile(file);
                updateTotalProgress();

            };

            uploader.on('all', function (type) {
                var stats;
                switch (type) {
                    case 'uploadFinished':
                        setState('confirm');
                        break;

                    case 'startUpload':
                        setState('uploading');
                        break;

                    case 'stopUpload':
                        setState('paused');
                        break;

                }
            });

            uploader.onError = function (code) {
                var text = ''
                switch (code) {
                    case 'Q_EXCEED_NUM_LIMIT':
                        text = '超过最大上传文件数量';
                        break;

                    case 'Q_EXCEED_SIZE_LIMIT':
                        text = '超过最大上传文件大小';
                        break;
                    case 'Q_TYPE_DENIED':
                        text = '上传文件类型错误';
                        break;
                    case 'F_EXCEED_SIZE':
                        text = '超过最大上传文件大小';
                        break;
                    default:
                        text = '发生错误,请重试';
                        break;
                }
                layer.msg(text);
            };

            $upload.on('click', function () {
                if ($(this).hasClass('disabled')) {
                    return false;
                }

                if (state === 'ready') {
                    uploader.upload();
                } else if (state === 'paused') {
                    uploader.upload();
                } else if (state === 'uploading') {
                    uploader.stop();
                }
            });

            $info.on('click', '.retry', function () {
                uploader.retry();
            });

            //$info.on('click', '.ignore', function () {
            //    layer.msg('todo');
            //});

            $upload.addClass('state-' + state);
            updateTotalProgress();




        },
        getUploader: function () {
            return uploader;
        },
        /*关闭上传框窗口后恢复上传框初始状态*/
        closeUploader: function () {
            // 移除所有缩略图并将上传文件移出上传序列
            for (var i = 0; i < uploader.getFiles().length; i++) {
                // 将图片从上传序列移除
                uploader.removeFile(uploader.getFiles()[i]);
                //uploader.removeFile(uploader.getFiles()[i], true);
                //delete uploader.getFiles()[i];
                // 将图片从缩略图容器移除
                var $li = $('#' + uploader.getFiles()[i].id);
                $li.off().remove();
            }

            //the.setState('pedding');

            //// 重置文件总个数和总大小
            //the.fileCount = 0;
            //the.fileSize = 0;
            // 重置uploader,目前只重置了文件队列
            uploader.reset();
            // 更新状态等,重新计算文件总个数和总大小
            //the.updateStatus();
        }
    }

}
.webuploader-container {
	position: relative;
}
.webuploader-element-invisible {
	position: absolute !important;
	clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
    clip: rect(1px,1px,1px,1px);
}
.webuploader-pick {
	position: relative;
	display: inline-block;
	cursor: pointer;
	background: #00b7ee;
	padding: 10px 15px;
	color: #fff;
	text-align: center;
	border-radius: 3px;
	overflow: hidden;
}
.webuploader-pick-hover {
	background: #00a2d4;
}

.webuploader-pick-disable {
	opacity: 0.6;
	pointer-events:none;
}

.wrapper {
    margin: 0 auto;

    margin: 1em;
    width: auto;
}

.uploader_container {
    border: 1px solid #dadada;
    /*color: #838383;*/
    font-size: 12px;
    margin-top: 10px;
    background-color: #FFF;
}

.uploader .queueList {
    margin: 20px;
}

.element-invisible {
    position: absolute !important;
    clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
    clip: rect(1px,1px,1px,1px);
}

.uploader .placeholder {
    border: 3px dashed #e6e6e6;
    min-height: 238px;
    padding-top: 158px;
    text-align: center;
    background: url(./image.png) center 93px no-repeat;
    color: #cccccc;
    font-size: 18px;
    position: relative;
}

.uploader .placeholder .webuploader-pick {
    font-size: 18px;
    background: #00b7ee;
    border-radius: 3px;
    line-height: 44px;
    padding: 0 30px;
    color: #fff;
    display: inline-block;
    margin: 20px auto;
    cursor: pointer;
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}

.uploader .placeholder .webuploader-pick-hover {
    background: #00a2d4;
}

.uploader .placeholder .flashTip {
    color: #666666;
    font-size: 12px;
    position: absolute;
    width: 100%;
    text-align: center;
    bottom: 20px;
}
.uploader .placeholder .flashTip a {
    color: #0785d1;
    text-decoration: none;
}
.uploader .placeholder .flashTip a:hover {
    text-decoration: underline;
}

.uploader .placeholder.webuploader-dnd-over {
    border-color: #999999;
}

.uploader .placeholder.webuploader-dnd-over.webuploader-dnd-denied {
    border-color: red;
}

.uploader .filelist {
    list-style: none;
    margin: 0;
    padding: 0;
}

.uploader .filelist:after {
    content: '';
    display: block;
    width: 0;
    height: 0;
    overflow: hidden;
    clear: both;
}

.uploader .filelist li {
    width: 110px;
    height: 110px;
    background: url(./bg.png) no-repeat;
    text-align: center;
    margin: 0 8px 20px 0;
    position: relative;
    display: inline;
    float: left;
    overflow: hidden;
    font-size: 12px;
}

.uploader .filelist li p.log {
    position: relative;
    top: -45px;
}

.uploader .filelist li p.title {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow : ellipsis;
    top: 5px;
    text-indent: 5px;
    text-align: left;
}

.uploader .filelist li p.progress {
    position: absolute;
    width: 100%;
    bottom: 0;
    left: 0;
    height: 8px;
    overflow: hidden;
    z-index: 50;
    background-color:rgba(0, 0, 0, 0)
}
.uploader .filelist li p.progress span {
    display: none;
    overflow: hidden;
    width: 0;
    height: 100%;
    background: #1483d8 url(./progress.png) repeat-x;

    -webit-transition: width 200ms linear;
    -moz-transition: width 200ms linear;
    -o-transition: width 200ms linear;
    -ms-transition: width 200ms linear;
    transition: width 200ms linear;

    -webkit-animation: progressmove 2s linear infinite;
    -moz-animation: progressmove 2s linear infinite;
    -o-animation: progressmove 2s linear infinite;
    -ms-animation: progressmove 2s linear infinite;
    animation: progressmove 2s linear infinite;

    -webkit-transform: translateZ(0);
}

@-webkit-keyframes progressmove {
    0% {
       background-position: 0 0;
    }
    100% {
       background-position: 17px 0;
    }
}
@-moz-keyframes progressmove {
    0% {
       background-position: 0 0;
    }
    100% {
       background-position: 17px 0;
    }
}
@keyframes progressmove {
    0% {
       background-position: 0 0;
    }
    100% {
       background-position: 17px 0;
    }
}

.uploader .filelist li p.imgWrap {
    position: relative;
    z-index: 2;
    line-height: 110px;
    vertical-align: middle;
    overflow: hidden;
    width: 110px;
    height: 110px;

    -webkit-transform-origin: 50% 50%;
    -moz-transform-origin: 50% 50%;
    -o-transform-origin: 50% 50%;
    -ms-transform-origin: 50% 50%;
    transform-origin: 50% 50%;

    -webit-transition: 200ms ease-out;
    -moz-transition: 200ms ease-out;
    -o-transition: 200ms ease-out;
    -ms-transition: 200ms ease-out;
    transition: 200ms ease-out;
}

.uploader .filelist li img {
    width: 100%;
}

.uploader .filelist li p.error {
    background: #f43838;
    color: #fff;
    position: absolute;
    bottom: 0;
    left: 0;
    height: 28px;
    line-height: 28px;
    width: 100%;
    z-index: 100;
}

.uploader .filelist li .success {
    display: block;
    position: absolute;
    left: 0;
    bottom: 0;
    height: 40px;
    width: 100%;
    z-index: 200;
    background: url(./success.png) no-repeat right bottom;
}

.uploader .filelist div.file-panel {
    position: absolute;
    height: 0;
    filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#80000000', endColorstr='#80000000')\0;
    background: rgba( 0, 0, 0, 0.5 );
    width: 100%;
    top: 0;
    left: 0;
    overflow: hidden;
    z-index: 300;
}

.uploader .filelist div.file-panel span {
    width: 24px;
    height: 24px;
    display: inline;
    float: right;
    text-indent: -9999px;
    overflow: hidden;
    background: url(./icons.png) no-repeat;
    margin: 5px 1px 1px;
    cursor: pointer;
}

.uploader .filelist div.file-panel span.rotateLeft {
    background-position: 0 -24px;
}
.uploader .filelist div.file-panel span.rotateLeft:hover {
    background-position: 0 0;
}

.uploader .filelist div.file-panel span.rotateRight {
    background-position: -24px -24px;
}
.uploader .filelist div.file-panel span.rotateRight:hover {
    background-position: -24px 0;
}

.uploader .filelist div.file-panel span.cancel {
    background-position: -48px -24px;
}
.uploader .filelist div.file-panel span.cancel:hover {
    background-position: -48px 0;
}

.uploader .statusBar {
    /*height: 63px;*/
    border-top: 1px solid #dadada;
    padding:0 20px 10px;
    line-height: 63px;
    vertical-align: middle;
    position: relative;
}

.uploader .statusBar .progress {
    border: 1px solid #1483d8;
    width: 198px;
    background: #fff;
    height: 18px;
    position: relative;
    display: inline-block;
    text-align: center;
    line-height: 20px;
    color: #6dbfff;
    position: relative;
    margin-right: 10px;
}
.uploader .statusBar .progress span.percentage {
    width: 0;
    height: 100%;
    left: 0;
    top: 0;
    background: #1483d8;
    position: absolute;
}
.uploader .statusBar .progress span.text {
    position: relative;
    z-index: 10;
}

.uploader .statusBar .info {
    display: inline-block;
    font-size: 14px;
    color: #666666;
}

.uploader .statusBar .btns {
    /*position: absolute;
    top: 10px;
    right: 20px;*/
    line-height: 40px;
    display: inline-block;
    float: right;
}

#filePicker2 {
    display: inline-block;
    float: left;
}

.uploader .statusBar .btns .webuploader-pick,
.uploader .statusBar .btns .uploadBtn,
.uploader .statusBar .btns .uploadBtn.state-uploading,
.uploader .statusBar .btns .uploadBtn.state-paused {
    background: #ffffff;
    border: 1px solid #cfcfcf;
    color: #565656;
    padding: 0 18px;
    display: inline-block;
    border-radius: 3px;
    margin-left: 10px;
    cursor: pointer;
    font-size: 14px;
    float: left;
}
.uploader .statusBar .btns .webuploader-pick-hover,
.uploader .statusBar .btns .uploadBtn:hover,
.uploader .statusBar .btns .uploadBtn.state-uploading:hover,
.uploader .statusBar .btns .uploadBtn.state-paused:hover {
    background: #f0f0f0;
}

.uploader .statusBar .btns .uploadBtn {
    background: #00b7ee;
    color: #fff;
    border-color: transparent;
}
.uploader .statusBar .btns .uploadBtn:hover {
    background: #00a2d4;
}

.uploader .statusBar .btns .uploadBtn.disabled {
    pointer-events: none;
    opacity: 0.6;
}

###调用

//实例化
var up = new Uploader()
//初始化
up.init($('#uploader'), {
    //jump: '/Task/Activity',
    pick: {
        id: '#filePicker',
        label: '点击选择文件'
    },
    pick2: '#filePicker2',//继续添加按钮
    dnd: '#dndArea',//拖拽的容器
    paste: '#uploader',//黏贴的容器
    swf: "~/Static/Library/webuploader/Uploader.swf",
    chunked: false,
    chunkSize: 512 * 1024,
    server: "@Url.Action("Upload", "Project")",
    formData: {
        XMID: '',
        XMZT: ''
    },

    disableGlobalDnd: true,
    fileNumLimit: 10,
    fileSizeLimit: 200 * 1024 * 1024, // 200 M
    fileSingleSizeLimit: 20 * 1024 * 1024 // 20 M
})
//发送前添加字段
up.getUploader().on('uploadBeforeSend', function (block, data) {
    data.XMID = $('#ViewTabFJ').attr('data-id');
    data.XMZT = $('#ViewTabFJ').attr('data-zt');
    data.Remark = $('#uploader [name="Remark"]').val();
});
//全部上传结束后触发回调
up.getUploader().on('uploadFinished', function () {
    layer.msg('上传成功');
    $('#ViewTabFJ').modal('hide')
});
//清空内容
up.closeUploader()

###说明

上传容器初始没宽度,会导致上传label大小计算异常,点击不到。
比如,上传容器放在bootstrap模态框,就会导致这个问题。
网上的解决思路大致有设定父容器宽度,修改源码计算公式,设死label大小位置
我自己取了个巧,用了个触发器,大家看一眼就明白了,不过应该是我第一个想到的把,比❤

$('#ViewTabFJ').on('click', '.webuploader-pick', function () {
    $('#filePicker label').trigger('click')
})

##ueditor.js

###封装
没封装,只写了一些辅助函数

function html_encode(html) {
    var temp = document.createElement("div");
    (temp.textContent != null) ? (temp.textContent = html) : (temp.innerText = html);
    var output = temp.innerHTML;
    temp = null;
    return output;
}
function html_decode(text) {
    var temp = document.createElement("div");
    temp.innerHTML = text;
    var output = temp.innerText || temp.textContent;
    temp = null;
    return output;
}
function replace_label(html, label) {
    var re
    for (var i in label) {
        re = new RegExp(label[i], "gim")
        html = html.replace(re, 'span')
    }

    return html
}

###调用

var ue = UE.getEditor('container');
$('#formSub').click(function () {
    var editor = UE.getEditor('container')
    var url = "@Url.Action("DoCreateReportTemplate", "Log")";
    var d = {
        'ReportTemplateName': $('#templateName').val(),
        'ReportTemplateType': $('#templateType').val(),
        'ReportTemplateTitle': $('#templateTitle').val(),
        'ReportTemplateContent': html_encode(editor.getContent())//编码,否则服务器可能会拒收
    }
    $.post(url, d)
        .done(function (data) {
            alert(data.Message)
        })
        .fail(function () {
            //layer.msg('网络错误!请重试。', { icon: 5 });
            aleet('网络错误!请重试。')
        });
})

###扩展
写了个让ueditor支持textarea的扩展,整理后补充。
一套下还有些麻烦的。。


我的2017

我的2017

一年又过去啦,发生了很多事,在此小记流水篇。

安稳的开头

碎碎念

上半年老老实实的在南京做着切图的活,编程上到了舒适区,着实轻松;生活上,友人相伴,虽然没什么钱,但也无忧无虑,真的很开心。

项目

基于vue的音乐播放器;

也过文章记录了,听说接口炸了= =、

日常切图;

git上的work_jushe仓库

手足无措的五月

碎碎念

其实我还蛮喜欢南京这座城的

安稳的日子还是结束了,5月初从聚奢网出来,租的房子月底到期,收拾了下自己,投简历找工作,面的单位不烧,有几家谈的也不错,可惜……都没了下文,没法子,回老家,再见南京!

狂飙突进的后续

碎碎念

新的开始

刚回家没两天,就被老妈逼着投简历,我心想太仓虽没什么机会,试一下还是可以的。就随手投了份、面试、上班、租房(离家蛮远)一气呵成。

该有的转折

在太仓呆了一个月,公司搬苏州去,我也跟着过去了,过起了我心中正常程序员的生活,有点小忙,也又长进。

项目

一个过于完整的OA系统。

bootstrap+jquery,功能太多了,甚至从里面拆出来几个什么经销商管理系统、商业管理系统来。

做这个项目的过程中写了篇复杂插件的文章;

两个微信公众号网页及配套后台

weui+zepto.js+微信JS-SDK,带前端路由,还有拉微信授权。一个叫微监测,两层CURD操作,带点数据处理展示,还带监控视频;另一个叫微解答,一个基本的问答页面。

两个APP的内嵌网页

基于vue全家桶的SPA,一个商城APP,里面的一些各种列表、详情、问答页面,不带路由,通信上和APP地址传参,正常调事件。一个智能设备APP,里面的商城、活动、晒图等模块,带路由,通信和上面一样(我想用JsBridge,APP那边不高兴)。

着两个做的不错,体积控制的也很好(500K上下),做的过程中学到了很多,有机会会分享下经验。

移动端官网

vue全家桶,性能体验都不错,信手拈来~

尾声,以及……

好啦好啦,就说到这里,对我来说是波澜起伏的一年,你的呢?

对了,在九月找回了那个她。

《javascript高级程序设计》笔记3(第6章 面向对象的程序设计,第7章 函数表达式)

90165 (1).jpg慧慧我老婆啊!

代码示例:https://github.com/ccchangkong/js/tree/master/js

第六章 面向对象的程序设计

对象:无序属性的集合,其属性可包含基本值、对象或函数。

6.1理解对象

属性类型:数据属性和访问器属性

数据属性有4个描述其行为的特性;

[[Configurable]]:能否通过delete删除从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性;

[[Enumerable]]:能否通过for-in循环返回属性;

[[Writable]]:能否修改属性的值;

[[Value]]:包含这个属性的数据值;

要修改属性的默认的特性,必须使用ES5的Object.defineProperty()方法;

var person {};
Object.defineProperty(person, 'name', {
writable: false,
value: "N"
});
alert(person.name); //N
person.name = "g";
alert(person.name); //N

writable: false,设置为只读,name属性的值不可更改;

var person {};
Object.defineProperty(person, 'name', {
configurable: false,
value: "N"
});
alert(person.name); //N
delete person.name;
alert(person.name); //N

configurable: false,不能从对象删除;

另外在调用Object.defineProperty()方法修改除writable之外的特性都会导致错误;如果不指定,则writable、configurable、enumerable的默认值都是false;

访问器属性:不包含数据值,包含一对geter(读取访问器属性调用)和seter(写入访问器属性调用)函数;vue.js实现用了这个。

四个特性

[[Configurable]]:能否通过delete删除从而重新定义属性,能否修改属性的特性,或能否把属性修改为数据属性;

[[Enumerable]]:能否通过for-in循环返回属性;

[[Get]]:读取访问器属性调用;

[[Set]]:写入访问器属性调用;

var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function() {
return this._year;
},
set: function(nV) {
if (nV > 2004) {
this._year = nV;
this.edition += nV - 2004;
}
}
});
book.year = 2005;
//alert(book.edition);
alert(book.year);
//alert(book._year);

_year表示只能通过对象方法访问的属性;

定义多个属性 Object.defineProperties()

var book = {};
Object.defineProperties(book, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function() {
return this._year;
},
set: function(nV) {
if (nV > 2004) {
this._year = nV;
this.edition += nV - 2004;
}
}
}
});
book.year = 2005;
//alert(book.edition);
alert(book.year);
//alert(book._year);

读取属性的特性

var book = {};
Object.defineProperties(book, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function() {
return this._year;
},
set: function(nV) {
if (nV > 2004) {
this._year = nV;
this.edition += nV - 2004;
}
}
}
});
var descriptor = Object.getOwnPropertyDescriptor(book, '_year');
alert(descriptor.value);//2004
alert(descriptor.configurable);//false

6.2 创建对象

工厂模式

//工厂模式
function createP(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
alert(this.name);
};
return o;
};
var p1 = createP("N", 29, "SOFT");

解决了创建多个相似对象的问题,但没有解决对象识别的问题。

构造函数模式

//构造函数模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert(this.name);
};
};
var p1 = new Person("N", 29, "SOFT");
alert(p1.constructor == Person);//true
alert(p1 instanceof Person);//true
alert(p1 instanceof Object);//true

constructor可以将他的实例标识为一种特定的类型;

//将构造函数当作函数
var p1 = new Person("N", 29, "SOFT");
p1.sayName();
//作为普通函数调用
Person("g", 29, "SOFT");
window.sayName();
//在另一个对象的作用域中调用
var o = new Object();
Person().call("k", 29, "SOFT");
o.sayName();
//构造函数的问题
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
};
function sayName() {
alert(this.name);
}

每定义一函数,也就是实例化了一个对象,那将构造函数内的函数转移到构造函数外,则美什么封装性可言了;

原型模式 prototype

function Person() {}
Person.prototype.name = "n";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
alert(this.name);
}
var p1 = new Person();
p1.sayName();//n
var p2 = new Person();
p2.sayName();//n
alert(p1.sayName()==p2.sayName());//true

Person.prototype.constructor->Person;

js.png

原型方法

isPrototypeOf(),getPrototypeOf()

function Person() {}
Person.prototype.name = "n";
Person.prototype.age = 29;
Person.prototype.sayName = function() {
alert(this.name);
}
var p1 = new Person();
var p2 = new Person();
p1.name = "g";
alert(p1.name); //g,来自实例
alert(p2.name); //n,来自原型
delete p1.name;
alert(p1.name); //n,来自原型

hasOwnPrototype()检测一个属性是否存在于实例中,为对象实例添加一个属性时,这个属性会屏蔽原型对象中保存的同名属性;

in操作符 "name" in p1检测一个属性是否存在于实例中或其属性上;

检测属性是存在于对象还是原型上

function hasPrototypeProperty(object, name) {
return !object.hasOwnProperty(name) && (name in object);
}

for-in 返回可访问、可枚举(enymerated)的属性(包括实例属性及原型);

Object.keys() 返回对象参数所以可枚举的实例属性为一个字符串;

得到所有实例属性,包含了不可枚举的constructor属性;

var keys = Object.getOwnPropertyNames(Person.prototype);

更简单的原型语法

//更简单的原型语法,但实例的constructor属性不再指向Person了
function Person() {}
Person.prototype = {
name: "n",
age: 29,
sayName: function() {
alert(this.name);
}
};
function Person() {}
Person.prototype = {
constructor: Person, //
name: "n",
age: 29,
sayName: function() {
alert(this.name);
}
};
function Person() {}
Person.prototype = {
name: "n",
age: 29,
sayName: function() {
alert(this.name);
}
};
Object.defineProperty(Person.prototype, "constructor", { //ES5
enumerable: false,
value: Person
});

原型的动态性

可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果重写了整个原型对象,就将联系切断了;

原生对象的原型

慎重

原生对象的问题

//原型对象的问题
function Person() {}
Person.prototype = {
constructor: Person,
name: "n",
age: 29,
friends: ["a", "b", "c"],
sayName: function() {
alert(this.name);
}
};
var p1 = new Person();
var p2 = new Person();
p1.friends.push('d');
alert(p1.friends); //a,b,c,d
alert(p2.friends); //a,b,c,d
alert(p1.friends === p2.friends); //true

原型包含引用类型值就有问题了,friends数组存在于Person.prototype而非在P1中;

组合使用构造函数模式和原型模式

//组合使用构造函数模式和原型模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
friends: ["a", "b", "c"];
};
Person.prototype = {
constructor: Person,
sayName: function() {
alert(this.name);
}
};

构造函数模式用于定义实例属性(私有),原型模式用于定义方法和共享属性(共享)。

是目前在JS中使用最广泛,认同度最高的一种创建自定义类型的方法,事实上是用来定义引用类型的一种默认模式。

动态原型模式

//动态原型模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
friends: ["a", "b", "c"];
if (typeof(this.sayName) != "function") {
Person.prototype.sayName = function() {
alert(this.name);
};
}
};
var friend = new Person('n', 20, 'ss');
friend.sayName();

只在sayName()方法不存在的时候才会将其添加到原型之中,且这段代码只会在初次调用构造函数时才会执行;

不能使用对象字面量来重写原型。

寄生构造函数模式

//寄生构造函数模式
function Person(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
alert(this.name);
};
return o;
};
var friend = new Person("N", 29, "SOFT");
friend.sayName(); //N
function SpecialArray() {
var values = new Array();
values.push().apply(values, arguments);
values.toPipedString = function() {
return this.join("|");
};
return values;
}
var colors = new SpecialArray("red", "blue");
alert(colors.toPipedString());

封装创建对象的代码,然后返回新创建的对象。

由于与构造函数的原型没关系。不能用instanceof()来确定对象类型。

稳妥构造函数模式

没有公共属性,不引用this对象,也不用new来调用构造函数

//稳妥构造函数模式
function Person(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
alert(name);
};
return o;
};

6.3 继承

JS无法实现接口继承(函数没有签名),只支持实现继承,且主要依靠原型链

function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSubValue()); //true

js2.png

所以引用类型默认都继承了Object.

确定原型和实例的关系

//确定原型和实例的关系
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true

谨慎地定义方法

//谨慎
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType(); //继承
SubType.prototype = { //使用字面量添加方法,会导致上一行到吗失效
getSubValue: function() {
//
},
someOtherMethod: function() {
//
}
};
var instance = new SubType();
alert(instance.getSuperValue()); //error

原型链的问题

包含引用类型值的原型属性会被所有实例共享;

在创建子类型的实例时,不能向超类型的构造函数中传递参数;

//原型链的问题
function SuperType() {
this.colors = ["a", "b"]
}
function SubType() {}
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push('c');
alert(instance1.colors); //a,b,c
var instance2 = new SubType();
alert(instance2.colors); //a,b,c

借用构造函数

//借用构造函数
function SuperType() {
this.colors = ["a", "b"];
}
function SubType() {
SuperType.call(this); //借用
}
var instance1 = new SubType();
instance1.colors.push('c');
alert(instance1.colors); //a,b,c
var instance2 = new SubType();
alert(instance2.colors); //a,b
//优势
function SuperType(name) {
this.name = name;
}
function SubType() {
SuperType.call(this, 'n'); //继承并传参
this.age = 29; //实例属性
}
var instance = new SubType();
alert(instance.name, instance.age); //n,29

方法都在函数中定义,不利于函数复用。

组合继承

//组合继承
function SuperType(name) {
this.name = name;
this.colors = ["a", "b"];
}
SuperType.prototype.sayName = function() {
alert(this.name);
};
function SubType(name, age) {
SuperType.call(this, name); //继承属性
this.age = age;
}
SubType.prototype = new SuperType(); //继承方法
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
alert(this.age);
};
var instance1 = new SubType('n', 29);
instance1.colors.push('c');
alert(instance1.colors); //a,b,c
instance1.sayName(); //n
instance1.sayAge(); //29
var instance2 = new SubType('m', 27);
alert(instance1.colors); //a,b
instance1.sayName(); //m
instance1.sayAge(); //27

最常用的继承模式,instanceof()和isPrototypeOf()也能识别。

原型链继承

//原型式继承
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
var person = {
name: 'n',
friends: ['a', 'b']
};
var person1 = object(person);
person1.name = 'g';
person1.friends.push('c');
var person2 = object(person);
person2.name = 'e';
person2.friends.push('d');
alert(person.friends); //a,b,c,d
//Objcet.create()
var person = {
name: 'n',
friends: ['a', 'b']
};
var person1 = Object.create(person);
person1.name = 'g';
person1.friends.push('c');
var person2 = Object.create(person);
person2.name = 'e';
person2.friends.push('d');
alert(person.friends); //a,b,c,d
var person = {
name: 'n',
friends: ['a', 'b']
};
var person1 = Object.create(person, {
name: {
value: 'g'
}
});
alert(person1.name); //g

Object.create() 规范化的原型式继承;

引用类型值的属性始终都会共享。

寄生式继承

//寄生式继承
function createAnother(original) {
var clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function() { //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
};
var person = {
name: "n",
friends: ['a', 'b']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //hi

寄生组合式继承

//寄生组合式继承
function inheritPrototype(subType, superType) {
var prototype = objcet(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
function SuperType(name) {
this.name = name;
this.colors = ["a", "b"];
}
SuperType.prototype.sayName = function() {
alert(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
alert(this.age);
}

通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

是引用类型最理想的继承范式。

第七章 函数表达式

创建函数 

函数声明 function funName(){};

函数表达式 var funName-funtion(){};必须先赋值后调用

//不要这么做
if (condition) {
function sayHi() {
//1
}
} else {
function sayHi() {
//2
}
} //结果在不同的浏览器中不同
//可以这么做
var sayHi;
if (condition) {
sayHi = function() {
//1
};
} else {
sayHi = function() {
//2
};
}

7.1 递归

递归函数是在一个函数通过名字调用自身的情况下构成的

//递归
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num - 1);
}
}
//完善
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1) //防止factorial的更改
}
}
//严格模式
var factorial = (function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1) //防止factorial的更改
}
});

7.2 闭包

闭包指有权访问另一个函数作用域中的变量的函数

//闭包
function createComparisionFunction(propertyName) {
return function(object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
}

内部函数(一个匿名函数)访问了外部函数中的变量propertyName。

闭包与变量

function creatFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function() {
return i;
};
}
return result;
}

每个函数都引用着保存变量i的同一个变量对象,i最后的值为10;

function creatFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function(num) {
return function() {
return num;
}
}(i);
}
return result;
}

参数为按值传递的;

关于this对象

在全局函数中,this等于window;

当函数被被作为某个对象方法调用时,this等于那个对象;

匿名函数的执行环境具有全局性,this等于window;(顺带一提,ES6中箭头函数没有自己的this值)

可通过call()和apply()改变函数执行环境,使this指向其他对象;

//关于this对象
var name = "w";
var object = {
name: 'o',
getNameFunc: function() {
return function() {
return this.name;
};
}
};
alert(object.getNameFunc()); //w
var name = "w";
var object = {
name: 'o',
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
alert(object.getNameFunc()); //o

内存泄漏

//内存泄漏
function assignHandler() {
var element = document.getElementById("some");
var id = element.id;
element.onclick = function() {
alert(id);
};
element = null;
}

设为null切断联系

7.3 模仿块级作用域

用匿名函数模拟

//块级作用域
function outputNumbers(count) {
(function() {
for (var i = 0; i < count; i++) {
alert(i);
}
})();
alert(i); //error
};

ES6中可使用let

7.4 私有变量  

这块自己也没弄太明白。。。

私有变量

//私有变量
function MyObject() {
//私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
//特权方法
this.publicMethod = function() {
privateVariable++;
return privateFunction();
}
}

静态私有变量

//静态私有变量
function MyObject() {
//私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
//构造函数
MyObject = function() {
}; //此为全局变量
//特权方法
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
}
}

模块模式

//单例
var singleton = {
name: value,
method: function() {}
};
var singleton = function() {
//添加变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
//特权/共有方法和属性
return {
publicProperty: true,
publicMethod: function() {
privateVariable++;
return privateFunction();
}
};
}();
var application = function() {
//私有变量和函数
var components = new Array();
//初始化
components.push(new BaseComponent());
//公共
return {
getComponentCount: function() {
return components.length;
},
registerComponent: function(component) {
if (typeof component == "object") {
components.push(component);
}
}
}
}();

增强的模块模式

//增强的模块模式
var singleton = function() {
//添加变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
//创建对象
var object = new CustomType();
//特权/共有方法和属性
object.publicProperty = true,
object.publicMethod = function() {
privateVariable++;
return privateFunction();
};
return object;
}();
var application = function() {
//私有变量和函数
var components = new Array();
//初始化
components.push(new BaseComponent());
var app = new BaseComponent();
//公共
app.getComponentCount = function() {
return components.length;
};
app.registerComponent = function(component) {
if (typeof component == "object") {
components.push(component);
}
};;
return app;
}();

一个slider vue模块

blog

自己做的播放器项目里的一个小组件

代码

vue文件地址,额,还是全贴出来把~

<template>
  <div class="slider">
    {{inputValue}}
        <div class="slider-track"  ref="bar" @click.self='btnclick'>
          <div ref="step" class="slider-fill"></div>
          <span class="slider-thumb"  ref="btn" @mousedown='btndown' @touchstart="btnth">    
          </span>
        </div>
  </div>
</template>

<script>
export default {
  name: 'slider',
  props: {
    value: {
      type: [Number, String],
      default: 0
    },
    max: {
      type: Number,
      default: 100
    }
  },
  data () {
    return {
      x: '',
      l: '',
      flag: false,
      inputValue: this.value
    }
  },
  computed: {
    width () {
      return this.inputValue * (this.$refs.bar.offsetWidth / this.max) - this.$refs.btn.offsetWidth / 2
    },
    stepWidth () {
      return Math.max(0, this.width)
    }
  },
  mounted () {
    this.$refs.btn.style.left = this.width + 'px'
    this.$refs.step.style.width = this.stepWidth + 'px'
  },
  methods: {
    btnclick (e) {
      let x = e.clientX - this.$refs.bar.offsetLeft
      this.inputValue = Math.round((x / this.$refs.bar.offsetWidth) * 100)
      // var y = e.clientY - this.$refs.bar.offsetTop
    },
    maxW () {
      return this.$refs.bar.offsetWidth - this.$refs.btn.offsetWidth / 2
    },
    btndown (e) {
      this.x = (e || window.event).clientX
      this.l = this.$refs.btn.offsetLeft
      this.flag = true
      document.addEventListener('mousemove', this.btnmove)
      document.addEventListener('mouseup', this.btnup)
    },
    btnmove (e) {
      let m = Math
      let thisX = (e || window.event).clientX
      this.tTo(m, thisX)
      window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty()
    },
    btnup () {
      this.flag = false
      document.removeEventListener('mousemove', this.btnmove)
      document.removeEventListener('mouseup', this.btnup)
    },
    tTo (m, x) {
      let w = m.min(this.maxW(), m.max(-this.$refs.btn.offsetWidth / 2, this.l + (x - this.x)))
      this.inputValue = m.round(m.max(0, w / this.maxW()) * 100)
    },
    btnth (e) {
      this.x = (e || window.event).touches[0].clientX
      this.l = this.$refs.btn.offsetLeft
      this.flag = true
      document.addEventListener('touchmove', this.btnthmove)
      document.addEventListener('touchend', this.btnthup)
    },
    btnthmove (e) {
      let m = Math
      let thisX = (e || window.event).touches[0].clientX
      this.tTo(m, thisX)
    },
    btnthup () {
      this.flag = false
      document.removeEventListener('touchmove', this.btnthmove)
      document.removeEventListener('touchend', this.btnthup)
    }
  },
  watch: {
    inputValue (val) {
      this.$emit('input', val)
      this.$emit('change', val)
      this.$refs.btn.style.left = this.width + 'px'
      this.$refs.step.style.width = this.stepWidth + 'px'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
  .slider {
    font-size: 12px;
    line-height: 50px;
    position: relative;
    height: 50px;
    width: 200px;
    list-style: none;
  }
  .slider-thumb {
    background-color: black;
    width: 16px;
    height: 16px;
    position: absolute;
    left: -8px;
    top: -5px;
    cursor: pointer;
    border-radius: 50%;
    transition: 0.5s box-shadow;
  }
.slider-thumb.Act{
box-shadow: 0 0 5px #333;
}
  .slider-track {
    background: red;
    height: 5px;
    position: relative;
    font-size: 0px;
    cursor: pointer;
  }

  .slider-fill {
    background-color: blue;
    position: absolute;
    height: 5px;
    width: 0;
    left: 0;
    bottom: 0;
  }

</style>

数据流

img

DOM事件流

福利

sublime text3 实时刷新

请先安装Package Control!

按ctrl+shift+p,输install按enter,输入LiveReload,回车确定安装;

在浏览器中安装对应插件,chrome下,在商店里搜索LiveReload,扩展程序里第一个就是,或者直接戳这里,然后在管理扩展程序里livereload的“允许访问文件网址”的权限打开;

QQ图片20160728102559.png

img关闭状态

img开启状态

回到sublime,在preferences(首选项) -> Packge Settings (插件设置)-> LiveReload -> Settings - User

输入

{
    "enabled_plugins"[
        "SimpleReloadPlugin",
        "SimpleRefresh"
    ]
}

这里如果编辑的是Settings - Default的话,貌似不能保存,则每次打开sublime都要手动开启livereload(ctrl+shift+p>输入livereload找livereload:enable/disable plug-ins按enter>enable simple reload)。

到这里直接在浏览器里打开文件,在sublime里编辑对应文件,保存后就能实时刷新了!

配合view in brower(在浏览器中预览效果更佳),安装:ctrl+shift+p>view in brower按enter

CTRL + ALT + F - Firefox
CTRL + ALT + C - Chrome
CTRL + ALT + I - Internet Explorer
CTRL + ALT + S - Safari

也可在Settings - User里自己设置快捷键;

2016年8月7日补充:直接在首选项 ->按键绑定-用户设置 { "keys": ["f12"], "command": "open_in_browser" }即可,不需安装插件。

附录:插件推荐

emmet,不解释;

terminal,安装完后按ctrl+shift+t在文件位置打开cmd;

HTML-CSS-JS Prettify,代码格式整理,快捷键ctrl+shift+h;

我的前端面试题

我的前端面试题

前阵子去湖南帮忙面试,话说还是第一次正经面试别人,问题是老早以前就开始想了,在这里整理一下,答案只是简要,也不一定完全正确,如有错误,劳烦指点

1、初始页面准备

1.1 css重置如何处理?

怎么也该说个

*{
    margin: 0;
	padding: 0;
}

或者详细点的#5

1.2 常用meta标签?

页面不缩放,双核浏览器chrome核优先巴拉巴拉

1.3 平常页面兼容到什么程度,兼容性怎么处理?

从W3C或caniuse上查询巴拉巴拉

1.4 减少页面的复杂度?

合理的结构,合理使用伪元素巴拉巴拉

2、css

2.1 盒模型介绍下,引申双边框、多层边框

#6

2.2 定位问题,让一个div垂直左右居中

#4

2.3 布局问题,浮动导致的高度塌陷如何处理(清浮动)

.clearfix {*zoom:1;}
.clearfix:after {content: '';display: block;height: 0;overflow: hidden;clear: both;}

2.4 布局问题,inline-block布局的问题

高低问题,基线导致的,设置vertical-align

在元素宽度设置没问题的情况下,意外的宽度不够,幽灵字符问题,设置父元素font-size:0

2.5 布局问题,flex属性简写和反向排序(如果用到)

flexnone | [ flex-grow ] || [ flex-shrink ] || [ flex-basis ]
flex-direction: row-reverse

2.6 移动端适配

rem布局,响应式布局,移动浏览器和webview常见问题(ios点击事件不生效、安卓webview各种权限)

3、js

3.1 jQuery选择器,已经找到了id为test的元素,往下找class为a的所有元素,接着往上找最近的一个class为b的元素,在回退到上一步筛选操作前的结果

$('#test').find('.a').closest('.b').end()

3.2 jQuery动态事件绑定和解绑

.on().off()

3.3 js数组和数组的拼接

a=a.concat(b); 
a.push.apply(a,b);
a.push(...b) 

3.4 vue 组件通信(如果会)

propsevents和借助vuex

3.5 vue-router路由传参、路由懒加载(如果会)

params

import('./Foo.vue')

3.6 混合开发中与原生代码互相调用(如果会)

//调用原生代码暴露的接口
,appFun(state, { funName, val = null }) {
      if (state.dev.type == 2) {
        window.android[funName](val);
      } else if (state.dev.type == 1) {
        window.webkit.messageHandlers[funName].postMessage(val);
      }
    }
 
	// 把函数挂在window对象上来暴露给APP
    let _this = this;   
    window.setGeolocation = function(params) {
      _this.setGeolocation(params);
    };

或者

引用jsbridge

4、工程化(如果会)

4.1 开发中的跨域代理

proxyTable

4.1 webpack优化配置,减小打包体积,加快首屏展现速度

https://github.com/ccchangkong/article/blob/master/vue-cli%E4%BF%AE%E6%94%B9.md

https://ssr.vuejs.org/zh/#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E6%B8%B2%E6%9F%93-ssr-%EF%BC%9F


因为要做的工作以切图为主,所以js问题问的比较少,针对面试者做过的东西会相对的提些问题,如富文本(标签转义)、文件生成、票据打印(pt单位)、地图开发(坐标系、点线面操作)。。

话说我捣鼓过的东西不少啊

由Webpack+Vue的一个demo说说Webpack配置

blog
项目地址

由Webpack+Vue的一个demo说说Webpack配置

最近想做个基于vue的webApp,就先找了点webpack的demo看看,自己动手玩了下,坑也踩到几个,马马虎虎,写篇文章记录一下

目录结构

20160930

package.json注释

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server --inline --config webpack.dev.config.js",//开发
    "build": "webpack --progress --hide-modules --config webpack.prod.config.js"//发布
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.14.0",//编译es6
    "babel-loader": "^6.2.5",//es6加载器
    "babel-plugin-transform-runtime": "^6.15.0",//编译es6
    "babel-preset-es2015": "^6.14.0",//编译es6
    "css-loader": "^0.25.0",//css加载器
    "extract-text-webpack-plugin": "^1.0.1",//单独打包css的插件
    "file-loader": "^0.9.0",//用于加载文件
    "html-loader": "^0.4.4",//html加载器
    "html-webpack-plugin": "^2.22.0",//用于配置html模板
    "node-sass": "^3.9.3",//编译sass
    "sass-loader": "^4.0.1",//sass加载器
    "style-loader": "^0.13.1",//css加载器
    "url-loader": "^0.5.7",//file-loader的封装,用于加载图片之类的东西
    "vue": "^1.0.26",//vue
    "vue-hot-reload-api": "^2.0.6",//vue热加载
    "vue-html-loader": "^1.2.3",//加载vue中的html
    "vue-loader": "^8.5.2",//vue加载器
    "vue-router": "^0.7.13",//vue路由
    "vue-style-loader": "^1.0.0",//加载vue中的样式
    "webpack": "^1.13.2",//webpack
    "webpack-dev-server": "^1.15.1"//webpack服务器
  }
}

webpack.config.js

这里把webpack.config.js拆开来配置了,另外把启动命令写在package.json里啦。

webpack.base.config.js

var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin'); //简化操作HTMl的插件
var ExtractTextPlugin = require('extract-text-webpack-plugin'); //用来关联外部文件
module.exports = {
    entry: './src/entry.js',//入口文件
    output: {
        path: path.join(__dirname, './dist'),
        // publicPath: '', //打包后的url前缀
        // filename: 'bundle.js'
        filename: 'bundle.[hash].js'

    },
    resolve: {
        extensions: ['', '.js', '.scss'], //省略扩展名
        //模块别名定义,方便后续直接引用别名,无须多写长长的地址
        alias: {
            Css: './sass/style.scss', //后续直接 require('Css') 即可
        }
    },
    module: {
        loaders: [                
                //loader的编译顺序为从后往前
                //'style-loader!css-loader',可省略-loader,如'style!css'
                // 解析.vue文件
                { test: /\.vue$/, loader: 'vue' },
                //解析js文件, exclude为排除,query为编译为es2015,
                { test: /\.js$/, loader: "babel", exclude: /node_modules/, query: { presets: ['es2015'], plugins: ['transform-runtime'] } },

                { test: /\.css$/, loader: ExtractTextPlugin.extract('style', ['css']), exclude: /node_modules|bootstrap/, },

                //将小于8192字节的图片转换为base64
                { test: /\.(jpg|png)$/, loader: "url?limit=8192" },

                //解析scss文件
                { test: /\.scss$/, loader: ExtractTextPlugin.extract('style', ['css', 'sass']), exclude: /node_modules|bootstrap/, },

                //html模板编译
                { test: /\.(html|tpl)$/, loader: 'html' }
            ] 
    },
    plugins: [ //这里变中括号了!
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: './src/tpl/index.html',
            inject: true //true | 'head' | 'body' | false  ,注入所有的资源到特定的 template 或者 templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元素的底部,'head' 将放置到 head 元素中。
        }),
        //重命名css文件
        new ExtractTextPlugin('[name].[chunkhash].css')
    ]
}

webpack.dev.config.js

var config = require('./webpack.base.config.js');//引入base

config.devtool = 'eval-source-map';//开启source-map

config.devServer = {
  noInfo: true,
  hot:false//将热更新关闭,否则自动刷新会出错,在package.json里不要把热更新加进去
};

module.exports = config;

webpack.prod.config.js

var config = require('./webpack.base.config.js');
config.plugins = (config.plugins || []).concat([//这么写好像是为了避免出错。。
    new Webpack.BannerPlugin("author:cc"),//添加字符串
    new Webpack.DefinePlugin({
        'process.env': {
            NODE_ENV: '"production"'
        }
    }),
    new Webpack.optimize.UglifyJsPlugin({//开启压缩
        compress: {
            warnings: false
        }
    })
]);
module.exports = config;

package.json

加了多余的逗号 ' , ' ,如

  "scripts": {
    "dev": "webpack-dev-server --inline --config webpack.dev.config.js",
    "build": "webpack --progress --hide-modules --config webpack.prod.config.js",
  }

应为

  "scripts": {
    "dev": "webpack-dev-server --inline --config webpack.dev.config.js",
    "build": "webpack --progress --hide-modules --config webpack.prod.config.js"
  }

编译sass可能会出错,把node-sass卸了重装可能有用。

webpack.config.js

不支持es6导入模块

output中filename设置[chunkhash]热刷新时会出错,可改为[hash]

resolve中alias,路径要搞清楚,是相对于使用了别名(即其所代表的模块)的模块的。

module拼错。。。。

loader里exclude排除啊,babel-loader那些杂七杂八的不能少

参考资料

webpack入坑之旅

进阶:构建具备版本管理能力的项目

一小时包教会 —— webpack 入门指南

webpack多页应用架构系列

国庆喽~

使用attr()给伪元素更优雅的赋值

使用attr()给伪元素更优雅的赋值

https://segmentfault.com/q/1010000006551803?_ea=1074082出自这个问题的我的回答,觉得值得一说,拿来凑更。

https://jsfiddle.net/ccchangkong/aykdu5o6/2/ 在线demo

    <div data-num='1' class="a1">33333</div>
    <div data-num='' class="a1">33333</div>
    .a1 {
        width: 200px;
        height: 200px;
        background-color: #333;
        color: #fff;
        position: relative;
        margin-top: 50px;
    }

    .num:after {
        content: attr(data-num);
        line-height: 50px;
        text-align: center;
        width: 50px;
        height: 50px;
        border-radius: 50%;
        background-color: red;
        position: absolute;
        top: -20px;
        right: -20px;
    }
        $(".a1").each(function() {
            if ($(this).attr('data-num') != '') {
                $(this).addClass('num');
            }
        });
        $('.a1').click(function() {
        let n=$(this).attr('data-num');
            $(this).addClass('num').attr('data-num', ++n);
        });

好像也没什么好说的,就这样吧~

img

img

img

手把手教你配置阿里云共享虚拟主机https

手把手教你配置阿里云共享虚拟主机https

想配https好久了,一开始折腾了几下没弄出来,卡在开启那边,后来有天瞎点点倒配出来了。。

注意哦

前置条件

1、申请证书

证书申请地址

img

依次点击Symantec->一个域名->免费型DV SSL

点击购买,确认后

点击证书控制台

也可以在控制台->安全(云盾)->SSL 证书(应用安全)进入

点击左侧未签发证书里的证书卡片的申请,在右侧弹窗中输入信息,点击下一步等待系统验证。

验证成功后可在已签发证书里看到(忘记多久了,反正蛮快的,我好像是吃了顿午饭就过了)

2、启用https

进入云虚拟主机控制台,可在控制台->域名与网站(万网)->云虚拟主机->管理

接着 站点信息->域名绑定

点击开启

选择云盾证书,确定后稍等片刻(我第一次申请就几分钟)

这样就好啦,直接输入原来http开头的网址会强制变成https

参考链接

虚拟主机HTTPS加密访问设置

《javascript高级程序设计》笔记6(第14章 表单脚本,第16章 HTML5脚本编程,第17章 错误处理与调试)

http://www.vastskycc.com/?id=21

第十四章 表单脚本

14.1 表单的基础知识

HTMLFormElement

属性/方法 介绍 HTML
acceptCharset 服务器能够处理的字符集 accept-charset
action 接受请求的URL action
elements 表单中所有控件的集合
enctype 请求的编码类型 enctype
length 表单中控件的类型
method 要发送的HTTP请求类型,通常是getpost method
name 表单的名称 name
reset() 将所有表单域重置为默认值
submit() 提交表单
target 用于发送请求和接收响应的窗口名称 target
//取得表单引用
var form = document.getElementById('form1');
var firstForm = document.forms[0]; //获取页面中的第一个表单
var myForm = document.forms['form2']; //取得页面中名称为‘form2’的表单,容易出错,将来浏览器可能不支持,不推荐

14.1.1 提交表单

<!--通用按钮-->
<input type="submit" value="submit form"/>
<!--自定义提交按钮-->
<button type="submit">submit form</button>
<!--图像按钮-->
<input type="image" src="demo.gif" />
//阻止表单提交
var form = document.getElementById('myForm');
EventUtil.addHandler(form, 'submit', function(event) {
    event = EventUtil.getEvent(event); //取得事件对象
    EventUtil.preventDefault(event); //阻止默认事件
});
var form = document.getElementById('myForm');
form.submit(); //提交表单,以此方式提交表单不会触发submit事件,因此记得先验证表单

问题:重复提交表单

解决办法:提交后禁用表单;取消后续的表单操作

14.1.2 重置表单

<!--通用重置按钮-->
<input type="reset" value="reset form" />
<!--自定义重置按钮-->
<button type="reset">reset form</button>
//阻止重置表单
var form = document.getElementById('myForm');
EventUtil.addHandler(form, 'reset', function(event) {
    event = EventUtil.getEvent(event); //取得事件对象
    EventUtil.preventDefault(event); //阻止默认事件
});
var form = document.getElementById('myForm');
form.reset(); //重置表单

14.1.3 表单字段

var form = document.getElementById('myForm');
var field1 = form.elements[0]; //取得表单中的第一个字段
var field2 = form.elements['textbox1']; //取得名为‘textbox1’的字段
var fieldCount = form.elements.length; //取得表单中包含的字段的数量
        <form action="" method="post" id='myForm'>
            <ul>
                <li><input type="radio" name="color" value="red" />red</li>
                <li><input type="radio" name="color" value="green" />green</li>
                <li><input type="radio" name="color" value="blue" />blue</li>
            </ul>
        </form>
var form = document.getElementById('myForm');
var colorFields = form.elements['color'];
alert(colorFields.length); //3

var firstColorField = colorFields[0];
var firstFormField = form.elements[0]; //尽可能用这种方式
alert(firstColorField === firstFormField); //true

公有的表单字段属性

属性 介绍
disabled 表示当前字段是否被禁用;布尔值
form 指向当前字段所属表单的指针;只读
name 当前字段的名称
readOnly 表示当前字段是否只读;布尔值
tabIndex 表示当前字段的切换序号
type 当前字段的类型
value 当前字段将被提交给服务器的值
var form = document.getElementById('myForm');
var field = form.elements[0];
//修改value属性
field.value = 'another value';
//检查form属性的值
alert(field.form === form); //true
//把焦点设置到当前字段
field.focus();
//禁用当前字段
field.disabled = true;
//修改type属性(不推荐,但对input来说时可行的)
field.type = 'checkbox';
//避免多次提交表单
EventUtil.addHandler(form, 'submit', function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    //取得提交按钮
    var btn = target.elements['submit-btn'];
    //禁用她
    btn.disabled = true;
});
说明 HTML示例 type属性的值
单选列表 "select-one"
多选列表 "select-multiple"
自定义按钮 "submit"
自定义非提交按钮 "button"
自定义重置按钮 "reset"
自定义提交按钮 "submit"

共有的表单字段方法

focus()andblur()

//为表单第一个字段调用focus()方法
EventUtil.addHandler(window, 'load', function(event) {
    document.forms[0].elements[0].focus();
    //如果其为input元素,且其type特性的值为‘hidden’,则会导致错误,css设置隐藏也会导致错误
});

//HTML5 ie10+
//<input type="text" autofocus/>
EventUtil.addHandler(window, 'load', function(event) {
    var element = document.forms[0].elements[0];
    if(element.autofocus !== true) {
        element.focus();
    }
});
//<input type="text" readonly/>
document.forms[0].elements[0].blur();

共有的表单字段事件

var textbox = document.forms[0].elements[0];
EventUtil.addHandler(textbox, 'focus', function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    if(target.style.backgroundColor != 'red') {
        target.style.backgroundColor = 'yellow';
    }
});
EventUtil.addHandler(textbox, 'blur', function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    if(/[^\d]/.test(target.value)) {
        target.style.backgroundColor = 'red';
    } else {
        target.style.backgroundColor = '';
    }
});
EventUtil.addHandler(textbox, 'change', function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    if(/[^\d]/.test(target.value)) {
        target.style.backgroundColor = 'red';
    } else {
        target.style.backgroundColor = '';
    }
});

14.2 文本框脚本

<input type="text" size="25" maxlength="50" value="texts"/>
<!--要表现文本框,须将type属性设置为text,显示25个字符,但输入不能超过50个字符-->
<textarea name="" rows="25" cols="5"></textarea>
var textbox = document.forms[0].elements['textbox1'];
alert(textbox.value);
textbox.value = 'new valuae';

14.2.1 选择文本

//选择文本
var textbox = document.forms[0].elements['textbox1'];
textbox.select();

EventUtil.addHandler(textbox, 'focus', function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    target.select();
});

选择事件

var textbox = document.forms[0].elements['textbox1'];
EventUtil.addHandler(textbox, 'select', function(event) {
    alert('text selected' + textbox.value);
});

取得选择的文本

//ie9+
function getSelectedText(textbox) {
    return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
}
//ie4-10
function getSelectedText(textbox) {
    if(typeof textbox.selectionStart == 'number') {
        return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
    } else if(document.selection) {
        return document.selection.createRange().text;
    }
}

选择部分文本

//选择部分文本
//ie9+
textbox.value = 'hello world';
//选择所有文本
textbox.setSelectionRange(0, textbox.value.length); //hello world
//选择前3个字符
textbox.setSelectionRange(0, 3); //'hel'
//选择第4到第6个字符
textbox.setSelectionRange(4, 7); //'o w'
function selectText(textbox, startIndex, stopIndex) {
    if(typeof textbox.selectionStart == 'number') {
        return textbox.value.substring(startIndex, stopIndex);
    } else if(textbox.createTextRange) {
        var range = textbox.createTextRange();
        range.collapse(true);
        range.moveStart('character', startIndex);
        range.moveEnd('character', stopIndex - startIndex);
        range.select();
    }
    textbox.focus();
}

//例子
textbox.value = 'hello world';
//选择所有文本
selectText(textbox, 0, textbox.value.length); //hello world
//选择前3个字符
selectText(textbox, 0, 3); //'hel'
//选择第4到6个字符
selectText(textbox, 4, 7); //'o w'

14.2.2 过滤输入

屏蔽字符

//屏蔽所有按键操作
EventUtil.addHandler(textbox, 'keypress', function(event) {
    event = EventUtil.getEvent(event);
    EventUtil.preventDefault(event);
});
//只允许输入数值
EventUtil.addHandler(textbox, 'keypress', function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    var charCode = EventUtil.getCharCode(event);
    if(!/\d/.test(String.fromCharCode(charCode)) && charCode > 9 && !event.ctrlKey) {
        EventUtil.preventDefault(event)
    }
});

操作剪贴板

var EventUtil = {
    //addclip
    getClipboardText: function(event) {
        var clipboardData = (event.clipboardData || window.clipboardData);
        return clipboardData.getData('text');
    },
    setClipboardText: function(event, value) {
        if(event.clipboardData) {
            return event.clipboardData.setData('text/plain', value);
        } else if(window.clipboardData) {
            return window.clipboardData.setData('text', value);
        }
    },
};
EventUtil.addHandler(textbox, 'paste', function(event) {
    event = EventUtil.getEvent(event);
    var text = EventUtil.getClipboardText(event);
    if(!/^\d*$/.test(text)) {
        EventUtil.preventDefault(event);
    }
});

14.2.3 自动切换焦点

//<input type="text" name="tel1" id="txtTel1" maxlength="3"/>
//<input type="text" name="tel2" id="txtTel2" maxlength="3"/>
//<input type="text" name="tel3" id="txtTel2" maxlength="4"/>

(function() {
    function tabForward(event) {
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        if(target.value.length == target.maxLength) {
            var form = target.form;
            for(var i = 0, len = form.elements.length; i < len; i++) {
                if(form.elements[i] == target) {
                    if(forms.elements[i + 1]) {
                        forms.elements[i + 1].focus();
                    }
                }
            }
        }
    }
    var textbox1 = document.getElementById('TextTel1');
    var textbox2 = document.getElementById('TextTel2');
    var textbox1 = document.getElementById('TextTel3');
    EventUtil.addHandler(textbox1, 'keyup', tabForward);
    EventUtil.addHandler(textbox2, 'keyup', tabForward);
    EventUtil.addHandler(textbox3, 'keyup', tabForward);

})();

14.2.4 HTML5约束验证API

必填字段

//<input type="text" name="username" required/>
//检查是否为必填字段
var isUsernameRequired = document.forms[0].elements['username'].required;
//检查是否支持
var isRequiredSupported = 'required' in document.createElement('input');

其他输入类型

//其他输入类型
//<input type="email" name="email"/>
//<input type="url" name="homepage"/>

var input = document.createElement('input');
input.type = 'email';
var isEmailSupported = (input.type == 'email');

只是验证,不起阻止作用

数值范围

//用户只能输入0到100的值,且这个值必须时5的倍数;效果不一定
//<input type="number" name="count" min='0' max='100' step='5'/>
//ie10+
input.stepUp(); //+1
input.stepUp(5); //+5
input.stepDown(); //-1
input.stepDown(10); //-10

输入模式

//ie10+
//<input type="number" name="count" pattern='\d+'/>
var pattern = document.forms[0].elements['count'].pattern;
var isPatternSupported = 'pattern' in document.createElement('input');

检测有效性

//表单字段
if(document.forms[0].elements[0].checkValidity()) {
    //continue
} else {
    //
}
//表单
if(document.forms[0].checkValidity()) {
    //continue
} else {
    //
}

if(input.validity && !input.validity.valid) {
    if(input.validity.valueMissing) {
        alert('please specify a value')
    } else if(input.validity.typeMismatch) {
        alert('please enter an email address')
    } else {
        alert('value is invalid');
    }
}

禁用验证

//<form novalidate></form>
document.forms[0].noValidate = true; //禁用验证

//<form novalidate><input type='submit' formnovalidate name='btn'></form>
document.forms[0].elements['btn'].formNoValidate = true;

14.3 选择框脚本

<select name="selects" id='selectss'>
    <option value="aa">1</option>
    <option>2</option>  
    <option value="">3</option>
</select>

如果用户选择了其中第一项,则选择框的值就是aa;选中第二项则为2;选中第三项则为空

var selectbox = document.forms[0].elements['selects'];
//不推荐
var text = selectbox.options[0].firstChild.nodeValue; //选项的文本值
var value = selectbox.options[0].getAttribute('value'); //选项的值
//推荐
var text = selectbox.options.text; //选项的文本值
var value = selectbox.options.value; //选项的值

14.3.1 选择选项

var selectedOption = selectbox.options[selectbox.selectedIndex];
//选中项之后
var selectedIndex = selectbox.selectedIndex;
var selectedOption = selectbox.options[selectedIndex];
console.log('selected index:' + selectedIndex + '\nSelected text:' + selectedOption.text + '\nSelected value:' + selectedOption.value); //索引、文本、值

selectbox.options[0].selected = true;

function getSelectedOptions(selectbox) {
    var result = new Array();
    var option = null;
    for(var i = 0, len = selectbox.options.length; i < len; i++) {
        option = selectbox.options[i];
        if(option.selected) {
            result.push(option);
        }
    }
    return result;
}

var selectbox = document.getElementById('selectss');
var selectedOptions = getSelectedOptions(selectbox);
var message = '';
for(var i = 0, len = selectedOptions.length; i < len; i++) {
    message += 'selected index:' + selectedOptions[i].index + '\nSelected text:' + selectedOptions[i].text + '\nSelected value:' + selectedOptions[i].value + '\n\n';
}

14.3.2 添加选项

//DOM方法
var newOption = document.createElement('option');
newOption.appendChild(document.createTextNode('Option text'));
newOption.setAttribute('value', 'Option value');
selectbox.appendChild(newOption);

//构造函数方法
var newOption = new Option('Option text', 'Option value');
selectbox.appendChild(newOption);

//add方法
var newOption = new Option('Option text', 'Option value');
selectbox.add(newOption, undefined); //最佳方案

14.3.3 移除选项

//移除第一个选项
selectbox.removeChild(selectbox.options[0]);
selectbox.remove(0);
selectbox.options[0] = null;

//清除所有选项
function clearSelectbox(selectbox) {
    for(var i = 0, len = selectbox.options.length; i < len; i++) {
        selectbox.remove(i);
    }
}

14.3.4 移动和重排选项

//将第一个选择框的第一个选项移动到第二个选择框
var selectbox1 = document.getElementById('selectbox1');
var selectbox2 = document.getElementById('selectbox2');
selectbox2.appendChild(selectbox1.options[0]);

//DOM方法
var optionToMove = selectbox.options[1];
selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index - 1]);
var optionToMove = selectbox.options[1];
selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index + 2]);

14.4 表单序列化

//表单序列化
function serialize(form) {
    var parts = [],
        field = null,
        len,
        j,
        optLen,
        option,
        optValue;
    for(i = 0, len = form.elements.length; i < len; i++) {
        field = form.elements[i];
        switch(field.type) {
            case 'select-one':
            case 'select-multiple':
                if(field.name.length) {
                    for(j = 0, optLen = field.options.length; j < optLen; j++) {
                        option = field.options[j];
                        if(option.selected) {
                            optValue = ''
                            if(option.hasAttribute) {
                                optValue = (option.hasAttribute('value') ? option.value : option.text);
                            } else {
                                optValue = (option.attributes['value'].specified ? option.value : option.text);
                            }
                            parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(optValue));
                        }
                    }
                }
                break;
            case undefined: //字段集
            case 'file': //文件输入
            case 'submit': //提交按钮
            case 'reset': //重置按钮
            case 'button': //自定义按钮
                break;
            case 'radio': //单选按钮
            case 'checkbox': //复选框
                if(!field.checked) {
                    break;
                }
                //执行默认操作
            default:
                //不包含没有名字的表单字段
                if(field.name.length) {
                    parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value));
                }
        }
    }
    return parts.join("&");
}

14.5 富文本编辑

<!--blank.html-->
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
</head>
<body>

</body>
</html>

<iframe src="blank.html" style='height:100px;width:100px;'></iframe>
    <script>
        EventUtil.addHandler(window,'load',function(){
            frames['richedit'].document.designMode='on';
        });
    </script>

14.5.1 使用contentEditable属性

//<div class='editable' id='richedit' contenteditable></div>
//打开和关闭
var div = document.getElementById('richedit');
div.contentEditable = 'true';//true,false,inherit

14.5.2 操作富文本

document.execCommand

14.5.3 富文本选区

iframe的getSelection()

14.5.4 表单与富文本

//提交
EventUtil.addHandler(form,'submit',function (event) {
    event=EventUtil.getEvent(event);
    var target=EventUtil.getTarget(event);
    target.elements['comments'].value=frames['richedit'].document.body.innerHTML;
});
EventUtil.addHandler(form,'submit',function (event) {
    event=EventUtil.getEvent(event);
    var target=EventUtil.getTarget(event);
    target.elements['comments'].value=document.getElementById('richedit').innerHTML;
});

第十六章 HTML5脚本编程

16.1 跨文档消息传递

var iframeWindow = document.getElementById('myFrame').contentWindow;
iframeWindow.postMessage('a secret', 'http://www.wrox.com');

EventUtil.addHandler(window, 'message', function(event) {
    //确保发送的域是已知的域
    if(event.origin == 'http://www.wrox.com') {
        //处理接收到的数据
        processMessage(event.data);

        //可选:向来源窗口发送回执
        event.source.postMessage('received', 'http://p2p.wrox.com');
    }
});

16.2 原生拖放

16.2.1 拖放事件

拖动某元素:1、dragstart;2、drag;3、dragend;事件的目标都是被拖动的元素

某元素被拖动到一个有效的放置目标时:1、dragenter;2、dragover;3、dragleavedrop;事件的目标都是作为放置目标的元素

16.2.2 自定义放置目标

//将#droptarget变为一个放置目标
var droptarget = document.getElementById('droptarget');
EventUtil.addHandler(droptarget, 'dragover', function(event) {
    EventUtil.preventDefault(event);
});
EventUtil.addHandler(droptarget, 'dragenter', function(event) {
    EventUtil.preventDefault(event);
});
//让ff不打开新窗口
EventUtil.addHandler(droptarget, 'drag', function(event) {
    EventUtil.preventDefault(event);
});

16.2.3 dataTransfer对象

//设置和接收文本数据
event.dataTransfer.setData('text', 'some test');
var text = event.dataTransfer.getData('text');
//设置和接收URL
event.dataTransfer.setData('URL', 'http://www.acfun.com');
var url = event.dataTransfer.getData('URL');

//兼容
var dataTransfer = event.dataTransfer;
//读取URL
var url = dataTransfer.getData('URL') || dataTransfer.getData('text/url-list');
//读取文本
var text = dataTransfer.getData('text');

16.2.4 dropEffect与effectAllowed

dropEffect:可以知道被拖动的元素能够执行哪种放置行为

effectAllowed:表示允许拖动元素的哪种dropEffect

16.2.5 可拖动

标签内的draggable设置为true,图像和链接默认时true

16.2.6 其他成员

addElement(element):为拖动操作添加一个元素

clearData(format):清除以特定格式保存的数据

setDragImage(element,x,y):指定一副图像,当拖动发生时,显示在光标下方。

types:当前保存的数据类型

16.3 媒体元素

videoaudio

指定多种格式的媒体来源时必须的

16.3.1 属性

一大堆

16.3.2 事件

一大堆

16.3.3 自定义媒体播放器

        <div class="mediaplayer">
            <div class="video">
                <video width="800" height="400" id="player">
                    <source src="myvideo.mp4" type="video/mp4"></source>
                    <source src="myvideo.ogv" type="video/ogg"></source>
                    <source src="myvideo.webm" type="video/webm"></source>
                    <object width="" height="" type="application/x-shockwave-flash" data="myvideo.swf">
                        <param name="movie" value="myvideo.swf" />
                        <param name="flashvars" value="autostart=true&amp;file=myvideo.swf" />
                    </object> 当前浏览器不支持 video直接播放,点击这里下载视频:
                    <a href="myvideo.webm">下载视频</a>
                </video>
            </div>
            <div class="controls">
                <input type="button" name="video-btn" id="video-btn" value="Play" />
                <span id="curtiome">0</span>/<span id="duration">0</span>
            </div>
        </div>
//取得元素的引用
var player = document.getElementById('player'),
    btn = document.getElementById('video-btn'),
    curtime = document.getElementById('curtime'),
    duratime = document.getElementById('duratime');
//更新播放时间
duratime.innerHTML = player.duration;
//为按钮添加事件处理程序
EventUtil.addHandler(btn, 'click', function(event) {
    if(player.paused) {
        player.play();
        btn.value = 'Pause';
    } else {
        player.pause();
        btn.value = 'Play';
    }
});
//定时更新当前时间
setInterval(function () {
    curtime.innerHTML=player.currentTime;
},250);

16.3.4 检测编解码器的支持情况

if (audio.canPlayType('audio/mpeg') {
    //
};

16.3.5 Audio类型

var audio = new Audio('sound.mp3');
EventUtil.addHandler(audio,'canplaythrough',function (event) {
    audio.play();   
});

16.4 历史状态管理

history.pushState({name:'N'},'N page' ,'n.html');//状态对象,新状态的标题,可选的相对URL

EventUtil.addHandler(window,'popstate',function (event) {
    var state=event.state;
    if (state) {
        processState(state);
    }
});
//更新当前状态
history.replaceState({name:'G'},'g page');

第十七章 错误处理与调试

17.1 浏览器报告的错误

F12开发者工具

17.1.1 IE

Tools(工具)->Internet Options(Internet选项)->Advanced(高级)->Display anotification about every script error(显示每个脚本错误的通知)

17.1.2 Firefox

Tools(工具)->Error Console(错误控制台)

17.1.3 Safari

Develop(开发)->Edit(编辑)->Preferences(偏好设置)->Advanced(高级)->Show develop menu in menubar(在菜单栏中显示‘开发’菜单)

17.1.4 Opera

Tools(工具)->Advanced(高级)->Error Console(错误控制台)

17.1.5 Chrome

右上角的自定义及控制->更多工具->开发者工具

17.2 错误处理

17.2.1 try-catch语句

try {
    //可能导致错误的代码
} catch(e) {
    //TODO handle the exception
}

try {
    cc.aa == 22;
} catch(e) {
    console.log(e.message);
}
//cc is not defined

finally子句

function testFinally() {
    try {
        return 2;
    } catch(e) {
        return 1;
    } finally {
        return 0;
    }
}
//finally始终都会执行

错误类型

错误类型 介绍
Error 基类型
EvalError 使用eval()函数发生异常时抛出
RangeError 数值超出相应范围时触发
ReferenceError 找不到对象
SyntaxError 把语法错误的字符串传入eval()函数
TypeError 变量的类型不符合要求
URIError 在使用encodeURI()或decodeURI(),URI格式不正确
try {
    someFunction();
} catch(e) {
    if(e instanceof TypeError) {
        //
    } else if(e instanceof ReferenceError) {
        //
    } else {
        //
    }
}

合理使用try-catch

适合处理我们无法控制的错误

17.2.2 抛出错误

遇到throw操作符时,代码会立即停止执行,除非在try-catch

//自定义错误消息
throw new Error('Something bad happend');

//自定义错误类型
function CustomError(msg) {
this.name = 'CustomError';
this.message = msg;
}
CustomError.prototype = new Error();
throw new CustomError('My msg';)


#### 抛出错误的时机

```js
//时机
function process(values) {
    if(!(values instanceof Array)) {
        throw new Error('process():Argument must be an array');
    }
    value.sort();
    for(var i = 0, len = values.length; i < len; i++) {
        if(values[i] > 100) {
            return values[i];
        }
    }
    return -1;
};

抛出错误与使用try-catch

捕获错误的目的在于避免浏览器以默认方式处理它们;

抛出错误的目的在于提供错误发生具体原因的消息

17.2.3 错误(error)事件

//捕获所有的error事件
//尽量不要用
window.onerror = function(message, url, line) {
        console.log(message);
        return false;
    }
    //图片记载错误时
EventUtil.addHandler(image, 'error', function(event) {
    alert("img not loaded");
});

17.2.4 处理错误的策略

多学多做

17.2.5 常见的错误类型

类型转换错误

在使用==and!=操作符,ifforwhile等流控制语句中使用非布尔值时容易发生

alert(age == '5'); //t
alert(5 === '5'); //f
alert(1 == true); //t
alert(1 === true); //f
function concat(str1, str2, str3) {
    var result = str1 + str2;
    if(str3) { //绝对不要这么做
        result += str3;
    }
    return result;
}

function concat(str1, str2, str3) {
    var result = str1 + str2;
    if(typeof str3 == 'string') { //恰当的比较
        result += str3;
    }
    return result;
}

数据类型错误

//不安全的函数,任何非字符串值都会导致错误
function getQueryString(url) {
    var pos = url.indexOf('?');
    if(pos > -1) {
        return url.substring(pos + 1);
    }
    return '';
}

function getQueryString(url) {
    if(typeof url == 'string') { //通过检查类型确保安全
        var pos = url.indexOf('?');
        if(pos > -1) {
            return url.substring(pos + 1);
        }
    }
    return '';
}
//不安全的函数,任何非数组值都会导致错误
function reverseSort(values) {
    if(values) { //绝对不要这么做
        values.sort();
        values.reverse();
    }
}

//不安全的函数,任何非数组值都会导致错误
function reverseSort(values) {
    if(values != null) { //绝对不要这么做,只能确保相应的值不是null或undefined
        values.sort();
        values.reverse();
    }
}

//还是不安全,任何非数组值但有sort方法的都会导致错误
function reverseSort(values) {
    if(type values.sort == 'function') { //绝对不要这么做,只能确保相应的值不是null或undefined
        values.sort();
        values.reverse();
    }
}

//安全,非数组值将被忽略
function reverseSort(values) {
    if(values instanceof Array) {
        values.sort();
        values.reverse();
    }
}

通信错误

//url
function addQueryStringArg(url, name, value) {
    if(url.indexOf('?') == -1) {
        url += '?';
    } else {
        url += '&';
    }
    url += encodeURIComponent(name) + '=' + encodeURIComponent(value);
    return url;
}

17.2.6 区分致命错误和非致命错误

非致命错误:不影响用户的主要任务;只影响页面的一部分;可以恢复;重复相同操作可以消除错误。

致命错误:应用程序根本无法继续运行;错误明显影响到了用户的主要操作;会导致其他连带错误。

for(var i = 0, len = mods.length; i < len; i++) {
    mods[i].init(); //可能会导致致命错误,一个init方法出错,后面的都将无法在进行
}
for(var i = 0, len = mods.length; i < len; i++) {
    try {
        mods[i].init(); //可能会导致致命错误
    } catch(e) {
        //TODO handle the exception
    }
}

17.2.7 把错误纪录到服务器

function logError(sev, msg) {
    var img = new Image();
    img.src = 'lo.php?sev=' + encodeURIComponent(sev) + '&msg=' + encodeURIComponent(msg);
}
//使用Image对象兼容性好、可以避免跨域限制、不容易出错
//只要使用try-catch语句就该把相应错误纪录到日志中
for(var i = 0, len = mods.length; i < len; i++) {
    try {
        mods[i].init();
    } catch(e) {
        logError('nonfatal', 'Module init failed:' + e.message);
    }
}

17.3 调试技术

少用alert(),免得忘记删了

17.3.1 将消息纪录到控制台

//console
function log(msg) {
    if(typeof(console) == "object") {
        console.log(msg);
    } else if(typeof(opera) == "object") {
        opera.postError(msg);
    } else if(typeof java == 'object' && typeof java.lang == "object") {
        java.lang.System.out.println(msg);
    }
}

17.3.2 将消息纪录到当前页面

function log(msg) {
    var console = document.getElementById('debuginfo');
    if(console === null) {
        console = document.createElement('div');
        console.id = 'debuginfo';
        console.style.border = '1px solid silver';
        console.style.position = 'absolute';
        console.style.top = '0px';
        console.style.right = '0px';
        document.body.appendChild(console);
    }
    console.innerHTML += '<p>' + msg + '</p>';
}

17.3.3 抛出错误

function divide(num1, num2) {
    if(typeof num1 != 'number' || typeof num2 != 'number') {
        throw new Error('divide():both arguments must be numbers'); //抛出明确的错误原因
    }
    return num1 / num2;
}
function assert(condition, msg) {
    if(!condition) {
        throw new Error(msg);
    }
};

function divide(num1, num2) {
    assert(typeof num1 != 'number' || typeof num2 != 'number', 'divide():both arguments must be numbers');
    return num1 / num2;
};

17.4 常见的IE错误

17.4.1 操作终止

ie7-,在修改尚未加载完成的页面时,就会发生操作终止错误

appendChild()换成insertBefore()

17.4.2 无效字符

奇怪的字符

17.4.3 未找到成员

document.onclick = function() {
    var event = window.event;
    setTimeout(function() {
        event.returnValue = false; //未找到成员错误
    }, 1000);
};

17.4.4 未知运行时错误

span.innerHTML = '<div>Hi</div>'; //块状元素插入行内元素,是不规范的

17.4.5 语法错误

少个分号啦、括号不对应;

引用外部JS文件但没返回JS代码

17.4.6 系统无法找到指定资源

IE对URL路径的长度不能超过2048个字符

webkit限定,纯css实现穿透镂空文字效果

我在 segmentfault 上 CSS特效:如何让文字覆盖的背景区域变透明?写的回答,感觉写的还可以,就拿来凑文章了~

img

实现图上这样的镂空文字效果

img

<p>Red</p>
             p:after {
                position: absolute;
                z-index: -2;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                content: '';
                background-image: inherit;
            }

            p:before {
                position: absolute;
                z-index: -1;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                content: '';
                background-color: rgba(256, 256, 256, .5);
            }

            p {
                font-size: 120px;
                line-height: 600px;
                position: relative;
                display: block;
                width: 551px;
                overflow: hidden;
                text-align: center;
                color: #fff;
                -webkit-text-fill-color: transparent;
                background-image: url(a.png);
                -webkit-background-clip: text;            
            }

两个伪元素,一个真-背景图,一个半透明遮蔽层,主体**部分用了两个webkit限定属性,这两个属性看名字就知道了干啥用了就不解释了。

如果不支持这两个属性的话是这个样子的

QQ图片20160811150227.png

以上。

984a5dcad1c8a78666ad005e6f09c93d71cf50b5.jpg

《javascript高级程序设计》笔记7(20-25)

#20 JSON

JavaScript Object Notation,JS对象表示法

20.1 语法

JSON的语法可以表示以下三种类型的值

类型 介绍
简单值 可表示字符串、数值、布尔值和null,不支持undefined
对象 一组无序的键值对
数组 一组有序的值的列表

20.1.1 简单值

数值5和字符串,字符串必须要双引号

5
"hello world"

20.1.2 对象

//js对象
var person = {
    name: 'N',
    age: 29
};
var person = {
    "name": "N",
    "age": 29
};
//json对象
{
    "name": "N",
    "age": 29
}
//没有变量声明
//不需要分号

//嵌套
{
    "name": "N",
    "age": 29,
    "school": {
        "name": "College",
        "localtion": "SHAXI"
    }
}
//可以出现相同的属性

//双引号!!!!

20.1.3 数组

//js
var values = [25, 'hi', true];
//json
[25, 'hi', true]
//数组对象结合
[{
    "name": "N",
    "age": 29,
    "school": {
        "name": "College",
        "localtion": "SHAXI"
    }
}, {
    "name": "C",
    "age": 28,
    "school": {
        "name": "College",
        "localtion": "SHAXI"
    }
}]

对象和数组通常时JSON数据结构的最外层形式

20.2 解析与序列化

//解析后
people[1].name;

20.2.1 JSON对象

早期解析使用eval()函数

现代一般使用JSON对象,IE8+;

var jsonText = JSON.stringify(people);
//console.log(jsonText);->{"name":"C","age":28,"school":{"name":"College","localtion":"SHAXI"}}

var peopleCopy = JSON.parse(jsonText);
//people与peopleCopy没什么关系,是两个独立的对象

在序列化js对象时,所有函数及原型成员都会被有意忽略,不体现在结果中。此外,值为undefined的任何属性也都会被跳过。

20.2.2 序列化选项

1 过滤结果

var jsonText = JSON.stringify(people, ["name", "age"]);
//{"name":"C","age":28}
var people = {
    name: ["C"], //这里改成数组了
    age: 28,
    school: {
        name: "College",
        localtion: "SHAXI"
    }
}
var jsonText = JSON.stringify(people, function(key, value) {
    switch(key) {
        case "name":
            return value.join(".");
        case "age":
            return 18; //永远的18岁
        case "school":
            return undefined;
        default:
            return value;
    }
});
//console.log(jsonText);->{"name":"C","age":18}

2 字符串缩进

var people = {
    name: "C",
    age: 28,
    school: {
        name: "College",
        localtion: "SHAXI"
    }
}
var jsonText = JSON.stringify(people, null, 4); //缩进4空格
var jsonText = JSON.stringify(people, null, "--"); //缩进2个段划线

3 toJSON()方法

var people = {
    name: "C",
    age: 28,
    school: {
        name: "College",
        localtion: "SHAXI"
    },
    toJSON: function() {
        return this.name;
    }
};
var jsonText = JSON.stringify(people);
//console.log(jsonText);->"C"

20.2.3 解析选项

var people = {
    name: "C",
    age: 28,
    school: {
        name: "College",
        localtion: "SHAXI"
    },
    releaseDate: new Date(2011, 11, 11)
};
var jsonText = JSON.stringify(people);
//console.log(jsonText);
//->{"name":"C","age":28,"school":{"name":"College","localtion":"SHAXI"},"releaseDate":"2011-12-10T16:00:00.000Z"}
var peopleCopy=JSON.parse(jsonText,function (key,value) {
    if (key=='releaseDate') {
        return new Date(value);
    } else{
        return value;
    }
});
//console.log(peopleCopy.releaseDate.getFullYear());->2011

#21 Ajax与Comet

21.1 XMLHttpRequest对象

//适用于IE7之前的版本
function createXHR() {
    if(typeof(arguments.callee.activeXstring) != "string") {
        var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
            i, len;
        for(i = 0, len = versions.length; i < len; i++) {
            try {
                new ActiveXObject(versions[i]);
                arguments.callee.activeXString = versions[i];
                break;
            } catch(e) {
                //TODO handle the exception
            }
        }
    }
    return new ActiveXObject(arguments.callee.activeXString);
}
//ie7+
var xhr = new XMLHttpRequest();
//兼容
function createXHR() {
    if(typeof XMLHttpRequest != 'undefined') {
        return new XMLHttpRequest();
    } else if(typeof ActiveXObject != 'undefined') {
        if(typeof(arguments.callee.activeXstring) != "string") {
            var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                i, len;
            for(i = 0, len = versions.length; i < len; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch(e) {
                    //TODO handle the exception
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error('no xhr object available')
    }
}
var xhr = createXHR();

21.1.1 XHR对象

//use xhr
xhr.open('get', 'example.txt', false); //还没发送请求呢
xhr.open('get', 'example.txt', false);
xhr.send(null);
XHR属性 简介
responseText 作为响应主体被返回的文本
responseXML 如果响应的内容类型时xml。则这个属性将保存包含着响应数据的XML DOM文档
status 响应的HTTP状态
statusText HTTP状态的说明

status值为200则请求成功,304表示请求的资源并没有修改,可以用缓存

xhr.open('get', 'example.txt', false);
xhr.send(null);
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
    console.log(xhr.responseText);
} else {
    console.log("request was unsuccessful:" + xhr.status);
}

readyState的值为4表示已经接收到全部响应数据,可以使用了

var xhr = createXHR();
xhr.onreadystatechange = function() {
    if(xhr.readyState == 4) {
        if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
            console.log(xhr.responseText);
        } else {
            console.log("request was unsuccessful:" + xhr.status);
        }
    }
};
xhr.open('get', 'example.txt', false);
xhr.send(null);
//取消异步请求
xhr.abort();

21.1.2 HTTP头部信息

头部信息 简介
Accept 浏览器能够处理的内容类型
Accept-Charest 浏览器能够显示的字符集
Accept-Encoding 浏览器能够处理的压缩编码
Accept-Language 浏览器当前设置的语言
Connection 浏览器与服务器之间连接的类型
Cookie 当前页面设置的任何Cookie
Host 发出请求的页面所在的域
Referer 发出请求的页面的URl。规范里拼错了(referrer)
User-Agent 浏览器的用户代理字符串
var xhr = createXHR();
xhr.onreadystatechange = function() {
    if(xhr.readyState == 4) {
        if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
            console.log(xhr.responseText);
        } else {
            console.log("request was unsuccessful:" + xhr.status);
        }
    }
};
xhr.open('get', 'example.txt', false);
xhr.setRequestHeader("MyHeader", 'MyValue');
xhr.send(null);
//头部信息
var myHeader = xhr.getResponseHeader("MyHeader");
var allHeaders = xhr.getAllResponseHeaders();

21.1.3 GET请求

最常见的请求类型,最常用于向服务器查询某些信息

xhr.open('get', 'example.php?name1=value1&name2=value2', true);
//辅助函数
function addURLParam(url, name, value) {
    url += (url.indexOf('?') == -1 ? '?' : '&');
    url += encodeURIComponent(name) + '=' + encodeURIComponent(value);
    return url;
}

var url = 'example.php';
//添加参数
url = addURLParam(url, 'name', 'N');
url = addURLParam(url, 'book', 'js');
//初始化请求
xhr.open('get', url, false);

21.1.4 POST请求

通常用于向服务器发送应该被保存的数据

xhr.open('post', 'example.txt', true);
function submitData() {
    var xhr = createXHR();
    xhr.onreadystatechange = function() {
        if(xhr.readyState == 4) {
            if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
                console.log(xhr.responseText);
            } else {
                console.log("request was unsuccessful:" + xhr.status);
            }
        }
    };
    xhr.open('post', 'example.php', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    var form = document.getElementById('user-info');
    xhr.send(serialize(form));
}

21.2 XMLHttpRequest2级

21.2.1 FormData

//ie11+
var data = new FormData();
data.append('name', 'n');
var data = new FormData(document.forms[0]);
var xhr = createXHR();
xhr.onreadystatechange = function() {
    if(xhr.readyState == 4) {
        if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
            console.log(xhr.responseText);
        } else {
            console.log("request was unsuccessful:" + xhr.status);
        }
    }
};
xhr.open('post', 'example.php', true);
var form = document.getElementById('user-info');
xhr.send(new FormData(form));

21.2.2 超时设定

var xhr = createXHR();
xhr.onreadystatechange = function() {
    if(xhr.readyState == 4) {
        if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
            console.log(xhr.responseText);
        } else {
            console.log("request was unsuccessful:" + xhr.status);
        }
    }
};
xhr.open('post', 'example.php', true);
var form = document.getElementById('user-info');
xhr.send(new FormData(form));

21.2.3 overrideMimeType()方法

用于重写XHR响应的MIME类型

var xhr = createXHR();
xhr.open('get', 'example.php', true);
xhr.overrideMimeType('text/xml');
xhr.send(null);

21.3 进度事件

21.3.1 load事件

在接收到完整的响应数据时触发

//load
var xhr = createXHR();
xhr.onload = function() {
    if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
        console.log(xhr.responseText);
    } else {
        console.log("request was unsuccessful:" + xhr.status);
    }
};
xhr.open('get', 'example.php', true);
xhr.send(null);

21.3.2 progress事件

在接收响应期间持续不断的触发

//progress
var xhr = createXHR();
xhr.onload = function() {
    if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
        console.log(xhr.responseText);
    } else {
        console.log("request was unsuccessful:" + xhr.status);
    }
};
xhr.onprogress=function (event) {
    var diovStatus=document.getElementById('status');
    if (event.lengthComputable) {
        diovStatus.innerHTML='received'+event.position+'of'+event.totalSize+'bytes';
    }
};
xhr.open('get', 'example.php', true);
xhr.send(null);

21.4 跨源资源共享

默认的情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源

CORS(Cross-Origin Resource Sharin,跨源资源共享)

21.4.1 IE对CORS的实现

IE8中的XDR(XDomainRequest)类型.

21.4.2 其他浏览器对CORS的实现

使用XHR对象并在open()方法中传入绝对URL

21.4.3 Preflighted Reqeusts

CORS通过一种叫做Preflighted Requests的透明服务器验证机制支持开发人员使用自定义的头部、GET、或POST之外的方法,以及不同类型的主体内容。

21.4.4 带凭据的请求

withCredentials属性设置为true,可以指定某个请求应该发送凭据

21.4.5 跨浏览器的CORS

//cros
function createCORSRequest(method, url) {
    var xhr = new XMLHttpRequest();
    if('withCredentials' in xhr) {
        xhr.open(method, url, true);
    } else if(typeof XDomainRequest != 'undefined') {
        xhr = new XDomainRequest();
        xhr.open(method, url);
    } else {
        xhr = null;
    }
    return xhr;
}
var request = createCORSRequest('get', 'http://www.bilibili.com/video/av6255062/');
if(request) {
    request.onload = function() {
        //对request.responseText进行处理
    };
    request.send();
}

共同的属性、方法

属性、方法 简介
abort() 用于停止正在进行的请求
onerror 用于替代onreadystatechange检测错误
onload 用于替代onreadystatechange检测成功
responseText 用于取得响应内容
send() 用于发送请求

21.5 其他跨域技术

21.5.1 图像Ping

//img ping
var img = new Image();
img.onload = img.onerror = function() {
    alert('done');
};
img.src = 'http://www.bilibili.com/test?name=n';

图像Ping最常用于跟踪用户点击页面或动态广告曝光次数。

有两个缺点,一是只能发送GET请求,二是无法访问服务器响应文本。

只能用于浏览器与服务器间的单向通信

21.5.2 JSONP

JSON with padding

//JSONP
function handleResponse(response) {
    console.log('you are at IP address' + response.ip + ',which is in' + response.city + ',' + response.region_name);
}
var script = document.createElement('script');
script.src = 'http://freegeoip.net/json/?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);

与前者相比,能够直接访问响应文本,支持在浏览器与服务器之间双向通信

缺点:从其他域中加载代码执行,不一定安全;其次,要确定JSONP请求是否失败并不容易

21.5.3 Comet

更高级的Ajax技术,也称服务器推送,实现方式有长轮询和流

长轮询使用XHR对象和setTimeout就能实现

HTTP流需要服务器端配合

function createStreamingClient(url, progress, finished) {
    var xhr = new XMLHttpRequest(),
        received = 0;
    xhr.open('get'.url, true);
    xhr.onreadystatechange = function() {
        var result;
        if(xhr.readyState == 3) {
            //只取得最新数据并调整计数器
            result = xhr.responseText.substring(received);
            received += result.length;
            //调用progress回调函数
            progress(result);
        } else if(xhr.readyState == 4) {
            finished(xhr.responseText);
        }
    };
    xhr.send(null);
    return xhr;
}
var client = createStreamingClient('streaming.php', function(data) {
    console.log('received:' + data);
}, function(data) {
    console.log('done');
});

21.5.4 服务器发送事件

sse(Sever-Sent Events)

1 SSE API

//sse
var source = new EventSource('myevents.php');

source.onmessage = function(event) {
    var data = event.data;
    //处理数据
};
source.close();

事件流

所谓的服务器事件会通过一个持久的HTTP响应发送,这个响应的MIME类型为text/event-stream

21.5.5 Web Sockets

21.6 安全

要验证发送请求者是否有权限访问相应的资源
#22 高级技巧

22.1 高级函数

22.1.1 安全的类型检测

typeofinstanceof的局限

function isArray(value) {
    return Object.prototype.toString.call(value) == '[object Array]';
}

function isFunction(value) {
    return Object.prototype.toString.call(value) == '[object Function]';
}

function isRegExp(value) {
    return Object.prototype.toString.call(value) == '[object RegExp]';
}

var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) = '[object JSON]';

22.1.2 作用域安全的构造函数

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
}
var person = new Person('n', 29, 'Engineer'); //没问题

var person = Person('n', 29, 'Engineer'); //this指向window
alert(window.name); //'n'
function Person(name, age, job) {
    if(this instanceof Person) {
        this.name = name;
        this.age = age;
        this.job = job;
    } else {
        return new Person(name, age, job);
    }
}
function Polygon(sides) {
    if(this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function() {
            return 0;
        };
    } else {
        return new Polygon(sides);
    }
}

function Rectangle(width, height) {
    Polygon.call(this, 2);
    this.width = width;
    this.height = height;
    this.getArea = function() {
        return this.width * this.height;
    };
}
var revt = new Rect(5, 10);
console.log(rect.sides); //undefined
function Polygon(sides) {
    if(this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function() {
            return 0;
        };
    } else {
        return new Polygon(sides);
    }
}

function Rectangle(width, height) {
    Polygon.call(this, 2);
    this.width = width;
    this.height = height;
    this.getArea = function() {
        return this.width * this.height;
    };
}
Rectangle.prototype = new Polygon();
var revt = new Rect(5, 10);
console.log(rect.sides); //2

22.1.3 惰性载入函数

避免执行不必要的代码

function createXHR() {
    if(typeof XMLHttpRequest != 'undefined') {
        return new XMLHttpRequest();
    } else if(typeof ActiveXObject != 'undefined') {
        if(typeof(arguments.callee.activeXstring) != "string") {
            var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                i, len;
            for(i = 0, len = versions.length; i < len; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch(e) {
                    //TODO handle the exception
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error('no xhr object available')
    }
}
function createXHR() {
    if(typeof XMLHttpRequest != 'undefined') {
        createXHR = function() {
            return new XMLHttpRequest();
        };
    } else if(typeof ActiveXObject != 'undefined') {
        createXHR = function() {
            if(typeof(arguments.callee.activeXstring) != "string") {
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                    i, len;
                for(i = 0, len = versions.length; i < len; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch(e) {
                        //TODO handle the exception
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        };

    } else {
        createXHR = function() {
            throw new Error('no xhr object available')

        };

    }
    return createXHR();
}
var createXHR = (function() {
    if(typeof XMLHttpRequest != 'undefined') {
        return function() {
            return new XMLHttpRequest();
        };
    } else if(typeof ActiveXObject != 'undefined') {
        return function() {
            if(typeof(arguments.callee.activeXstring) != "string") {
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                    i, len;
                for(i = 0, len = versions.length; i < len; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch(e) {
                        //TODO handle the exception
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        };

    } else {
        return function() {
            throw new Error('no xhr object available')
        };
    }
})();

22.1.4 函数绑定

var handler = {
    msg: 'event handled',
    handleClick: function(event) {
        console.log(this.msg);
    }
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', function(event) {
    handler.handleClick(event);//使用闭包保存this
});
function bind(fn, context) {
    return function() {
        return fn.apply(context, arguments);
    };
}

var handler = {
    msg: 'event handled',
    handleClick: function(event) {
        console.log(this.msg);
    }
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', bind(handler.handleClick, handler));

var handler = {
    msg: 'event handled',
    handleClick: function(event) {
        console.log(this.msg + ':' + event.type);
    }
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', bind(handler.handleClick, handler));
//es5
var handler = {
    msg: 'event handled',
    handleClick: function(event) {
        console.log(this.msg + ':' + event.type);
    }
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', handler.handleClick.bind(handler));

22.1.5 函数柯里化

//currying
function add(num1, num2) {
    return num1 + num2;
}

function curriedAdd(num2) {
    return add(5, num2);
}
console.log(add(2, 3)); //5
console.log(curriedAdd(3)); //8
function curry(fn) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    };
} //没考虑执行环境
//demo
function add(num1, num2) {
    return num1 + num2;
}
var curriedAdd = curry(add, 5);
console.log(curriedAdd(3)); //8

var curriedAdd = curry(add, 5, 12);
console.log(curriedAdd()); //17
function bind(fn, context) {
    var args = Array.prototype.slice.call(arguments, 2);
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(content, finalArgs);
    };
}
var handler = {
    msg: 'event handled',
    handleClick: function(name, event) {
        console.log(this.msg + ':' + name + ':' + event.type);
    }
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', bind(handler.handleClick, handler, 'my-btn'));

//es5 bind

var handler = {
    msg: 'event handled',
    handleClick: function(name, event) {
        console.log(this.msg + ':' + name + ':' + event.type);
    }
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn, 'click', handler.handleClick.bind(handler, 'my-btn'));

22.2 防篡改对象

22.2.1 不可扩展对象

Object.preventExtensions(object),不能给对象添加新属性和方法了

22.2.2 密封的对象

Object.seal(objcet),不可扩展,且已有成员的[[Configurable]]特性将被设置为false

22.2.3 冻结的对象

Object.freeze(objcet),不可扩展,又是密封的,且对象的[[Writable]]特性将被设置为false

22.3 高级定时器

22.3.1 重复的定时器

setTimeout(function() {
    //处理中
    setTimeout(arguments.callee, interval);
}, interval);

在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔

22.3.2 Yielding Processes

//一般循环
for(var i = 0, len = data.length; i < len; i++) {
    process(data[i]);
}

如果不需要同步完成,且不需按顺序完成,则可以使用定时器分割这个循环,叫做数组分块

setTimeout(function() {
    //取出下一个条目并处理
    var item = array.shift();
    process(item);
    //若还有条目,再设置另一个定时器
    if(array.length > 0) {
        setTimeout(arguments.callee, 100);
    }
}, 100);
function chunk(array, process, context) {
    setTimeout(function() {
        //取出下一个条目并处理
        var item = array.shift();
        process(item);
        //若还有条目,再设置另一个定时器
        if(array.length > 0) {
            setTimeout(arguments.callee, 100);
        }
    }, 100);
}
chunk(data,concat(),prinValue);

22.3.3 函数节流

间隔执行函数,比如用在onresize事件处理程序之类的

var processor = {
    timeoutId: null,
    //实际进行处理的方法
    performProcessing: function() {
        //实际执行的代码
    },
    //初始处理调用的方法
    process: function() {
        clearTimeout(this.timeoutId);
        var that = this;
        this.timeoutId = setTimeout(function() {
            that.performProcessing();
        }, 100);
    }
};
//尝试开始执行
processor.process();
//简化
function throttle(method, context) {
    clearTimeout(method.tId);
    method.tId = setTimeout(function() {
        method.call(content);
    }, 100);
}
//demo
window.onresize = function() {
    var div = document.getElementById('myDiv');
    dic.style.height = div.offsetWidth + 'px';
};

//butter
function resizeDiv() {
    var div = document.getElementById('myDiv');
    dic.style.height = div.offsetWidth + 'px';
}
window.onresize = function() {
    throttle(resizeDiv);

22.4 自定义事件

function EventTarget() {
    this.handlers = {};
}

EventTarget.prototype = {
    constructor: EventTarget,
    addHandler: function(type, handler) {
        if(typeof this.handlers[type] == 'undefined') {
            this.handlers[type] = [];
        }
        this.handlers[type].push(handler);
    },
    fire: function(event) {
        if(!event.target) {
            event.target = this;
        }
        if(this.handlers[event.type] instanceof Array) {
            var handlers = this.handlers[event.type];
            for(var i = 0, len = handlers.length; i < len; i++) {
                handlers[i](event);
            }
        }
    },
    removeHandler: function(type, handler) {
        if(this.handlers[type] instanceof Array) {
            if(var i = 0, len = handlers.length; i < len; i++) {
                if(handlers[i] === handler) {
                    break;
                }
            }
            handlers.splice(i, 1);
        }
    }
};
//demo
function handleMessage(event) {
    console.log('msg received:' + event.message);
}
//创建一个对象
var target = new EventTarget();
//添加一个事件处理程序
target.addHandler('message', handleMessage);
//触发事件
target.fire({
    type: 'message',
    message: 'hello'
});
//删除事件处理程序
target.removeHandler('message', handleMessage);
function Person(name, age) {
    EventTarget.call(this);
    this.name = name;
    this.age = age;
}
inheritPrototype(Person, EventTarget);
Person.prototype.say = function(message) {
    this.fire({
        type: 'message',
        message: 'hello'
    });
};
function handleMessage(event) {
    console.log(event.target.name + 'says:' + event.message);
}
//创建新person
var person = new Person('N', 22);
//添加一个事件处理程序
target.addHandler('message', handleMessage);
//再该对象上调用1个方法,它触发消息事件
person.say('hi');

22.5 拖放

//onmousemove事件
EventUtil.addHandler(document, 'mousemove', function(event) {
    var myDiv = document.getElementById('myDiv');
    myDiv.style.left = event.clientX + 'px';
    myDiv.style.top = event.clientY + 'px';
});
var DragDrop = function() {
    var dragging = null;

    function handleEvent(event) {
        //获取事件 和目标
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        //确定事件类型
        switch(event.type) {
            case 'mousedown':
                if(target.className.indexOf('draggable') > -1) {
                    dragging = target;
                }
                break;
            case 'mousemove':
                if(dragging != null) {
                    //指定位置
                    dragging.style.left = event.clientX + 'px';
                    dragging.style.top = event.clientY + 'px';
                }
                break;
            case 'mouseup':
                dragging = null;
                break;
            default:
                break;
        }
    };
    return {
        enable: function() {
            EventUtil.addHandler(document, 'mousedown', handleEvent);
            EventUtil.addHandler(document, 'mousemove', handleEvent);
            EventUtil.addHandler(document, 'mouseup', handleEvent);
        },
        disable: function() {
            EventUtil.removeHandler(document, 'mousedown', handleEvent);
            EventUtil.removeHandler(document, 'mousemove', handleEvent);
            EventUtil.removeHandler(document, 'mouseup', handleEvent);
        }
    }
}();

记得绝对定位哦

22.5.1 修缮拖动功能

鼠标跳动

var DragDrop = function() {
    var dragging = null,
        diffX = 0,
        diffY = 0;

    function handleEvent(event) {
        //获取事件 和目标
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        //确定事件类型
        switch(event.type) {
            case 'mousedown':
                if(target.className.indexOf('draggable') > -1) {
                    dragging = target;
                    diffX = event.clientX - target.offsetLeft;
                    diffY = event.clientY - target.offsetLTop;

                }
                break;
            case 'mousemove':
                if(dragging != null) {
                    //指定位置
                    dragging.style.left = (event.clientX - diffX) + 'px';
                    dragging.style.top = (event.clientY - diffY) + 'px';
                }
                break;
            case 'mouseup':
                dragging = null;
                break;
            default:
                break;
        }
    };
    return {
        enable: function() {
            EventUtil.addHandler(document, 'mousedown', handleEvent);
            EventUtil.addHandler(document, 'mousemove', handleEvent);
            EventUtil.addHandler(document, 'mouseup', handleEvent);
        },
        disable: function() {
            EventUtil.removeHandler(document, 'mousedown', handleEvent);
            EventUtil.removeHandler(document, 'mousemove', handleEvent);
            EventUtil.removeHandler(document, 'mouseup', handleEvent);
        }
    }
}();

22.5.2 添加自定义事件

var DragDrop = function() {
    var dragdrop = new EventTarget(),
        dragging = null,
        diffX = 0,
        diffY = 0;

    function handleEvent(event) {
        //获取事件 和目标
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        //确定事件类型
        switch(event.type) {
            case 'mousedown':
                if(target.className.indexOf('draggable') > -1) {
                    dragging = target;
                    diffX = event.clientX - target.offsetLeft;
                    diffY = event.clientY - target.offsetLTop;
                    dragdrop.fire({
                        type: 'dragstart',
                        target: dragging,
                        x: event.clientX,
                        y: event.clientY
                    });
                }
                break;
            case 'mousemove':
                if(dragging != null) {
                    //指定位置
                    dragging.style.left = (event.clientX - diffX) + 'px';
                    dragging.style.top = (event.clientY - diffY) + 'px';
                    //触发自定义事件
                    dragdrop.fire({
                        type: 'drag',
                        target: dragging,
                        x: event.clientX,
                        y: event.clientY
                    });
                }
                break;
            case 'mouseup':
                dragdrop.fire({
                    type: 'dragend',
                    target: dragging,
                    x: event.clientX,
                    y: event.clientY
                });
                dragging = null;
                break;
            default:
                break;
        }
    };

    dragdrop.enable = function() {
        EventUtil.addHandler(document, 'mousedown', handleEvent);
        EventUtil.addHandler(document, 'mousemove', handleEvent);
        EventUtil.addHandler(document, 'mouseup', handleEvent);
    };
    dragdrop.disable = function() {
        EventUtil.removeHandler(document, 'mousedown', handleEvent);
        EventUtil.removeHandler(document, 'mousemove', handleEvent);
        EventUtil.removeHandler(document, 'mouseup', handleEvent);
    };
    return dragdrop;
}();
DragDrop.addHandler('dragstart', function(event) {
    var status = document.getElementById('status');
    status.innerHTML = 'started dragging' + event.target.id;
});
DragDrop.addHandler('drags', function(event) {
    var status = document.getElementById('status');
    status.innerHTML = '<br/>dragged' + event.target.id + 'to(' + event.x + ',' + event.y + ')';
});
DragDrop.addHandler('dragend', function(event) {
    var status = document.getElementById('status');
    status.innerHTML = '<br/>dropped' + event.target.id + 'to(' + event.x + ',' + event.y + ')';
});

#23 离线应用与客户端存储

设备在不能上网的情况下仍然可以运行的应用

23.1 离线检测

//检测网络状态
if(navigator.onLine) {
    //正常工作
} else {
    //执行离线状态时的任务
}
EventUtil.addHandler(window, 'online', function() {
    console.log('online');
});
EventUtil.addHandler(window, 'offline', function() {
    console.log('offline');
});

23.2 应用缓存

applicationCache对象

23.3 数据存储

23.3.1 Cookie

1 限制

每个域的cookie总数是有限的,大小也有限制

2 cookie的构成

名称、值、域、路径、失效时间、安全标志

3 JS中的cookie

var CookieUtil = {
    get: function(name) {
        var cookieName = encodeURIComponent(name) + '=',
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null;
        if(cookieStart > -1) {
            var cookieEnd = document.cookie.indexOf(';', cookieStart);
            if(cookieEnd == -1) {
                cookieEnd = document.cookie.length;
            }
            cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));

        }
        return cookieValue;
    },
    set: function(name, value, expires, path, domain, secure) {
        var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value);
        if(expires instanceof Date) {
            cookieText += ';expires=' + expires.toGMTString();
        }
        if(path) {
            cookieText += ';path=' + path;
        }
        if(domain) {
            cookieText += ';domain=' + domain;
        }
        if(secure) {
            cookieText += ';secure';
        }
        document.cookie = cookieText;
    },
    unset: function(name, domain, secure) {
        this.set(name, '', new Date(0), path, domain, secure);
    }
};
//demo
//设置cookie
CookieUtil.set('name', 'N');
CookieUtil.set('book', 'jser');
//读取cookie的值
console.log(CookieUtil.get('name'));
console.log(CookieUtil.get('book'));
//删除cookie
CookieUtil.unset('name');
CookieUtil.unset('book');
//设置cookie,包括它的路径、域、失效日期
CookieUtil.set('name', 'n', '/books/projs/', 'www.wrox.com', new Date("Jan 1,2010"));
//删除刚刚设置的cookie
CookieUtil.unset('name', '/books/projs/', 'www.wrox.com');
//设置安全的cookie
CookieUtil.set('name', 'n', null, null, null, true);

4 子cookie

var SubCookieUtil = {
    get: function(name, subName) {
        var subCookies = this.getAll(name);
        if(subCookies) {
            return subCookies(subName);
        } else {
            return null;
        }
    },
    getAll: function(name) {
        var cookieName = encodeURIComponent(name) + '=',
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null,
            cookieEnd,
            subCookies,
            i,
            parts,
            result = {};
        if(cookieStart > -1) {
            cookieEnd = document.cookie.indexOf(';', cookieStart);
            if(cookieEnd == -1) {
                cookieEnd = document.cookie.length;
            }
            cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd);
            if(cookieValue.length > 0) {
                subCookies = cookieValue.split('&');
                for(i = 0, len = subCookies.length; i < len; i++) {
                    parts = subCookies[i].split('=');
                    result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
                }
                return result;
            }
        }
        return null;
    },
    set: function(name, subName, value, expires, path, domain, secure) {
        var subcookies = this.getAll(name) || {};
        subcookies[subName] = value;
        this.setAll(name, subcookies, expires, path, domain, secure)
    },
    setAll: function(name, subcookies, expires, path, domain, secure) {
        var cookieText = encodeURIComponent(name) + '=',
            subcookieParts = new Array(),
            subName;
        for(subName in subcookies) {
            if(subName.length > 0 && subcookies.hasOwnProperty(subName)) {
                subcookieParts.push(encodeURIComponent(subName) + '=' + encodeURIComponent(subcookies[subName]));
            }
        }
        if(subcookieParts > 0) {
            cookieText += subcookieParts.join('&');
            if(expires instanceof Date) {
                cookieText += ';expires=' + expires.toGMTString();
            }
            if(path) {
                cookieText += ';path=' + path;
            }
            if(domain) {
                cookieText += ';domain=' + domain;
            }
            if(secure) {
                cookieText += ';secure';
            }

        } else {
            cookieText += ';expires=' + (new Date(0)).toGMTString();
        }
        document.cookie = cookieText;
    },
    unset: function(name, subName, path, domain, secure) {
        var subcookies = this.getAll(name);
        if(subcookies) {
            delete subcookies[subName];
            this.setAll(name, subcookies, null, path, domain, secure);
        }
    },
    unsetAll: function(name, path, domain, secure) {
        this.set(name, null, new Date(0), path, domain, secure);
    }
};
//document.cookie=data=name=N&book=JS%20er
//取得全部子cookie
var data = SubCookieUtil.getAll('data');
console.log(data.name); //N
console.log(data.book); //JS er
//逐个获取子cookie
console.log(SubCookieUtil.get('data', 'name')); //N
console.log(SubCookieUtil.get('data', 'book')); //JS er

//设置两个cookie
SubCookieUtil.set('data', 'name', 'N');
SubCookieUtil.set('data', 'book', 'jsjs');
//设置全部子cookie和失效日期
SubCookieUtil.setAll('data', {
    'data': 'name',
    'data': 'book'
}, new Date('Jan 1,2010'));
//修改名字的值,并修改cookie的失效日期
SubCookieUtil.setAll('data', 'name', 'Mc', new Date('Jan 1,2010'));

//仅删除名为name的子cookie
SubCookieUtil.unset('data', 'name');
//删除整个cookie
SubCookieUtil.unsetAll('data');

5 关于cookie的思考

少存点

23.3.2 IE用户数据

//<div style="behavior:url(#default#userData)" id=dadaStore''></div>

//保存
var dataStore = document.getElementById('dataStore');
dataStore.setAttribute('name', 'N');
dataStore.setAttribute('book', 'JSer');
dataStore.save('BookInfo');
//获取
dataStore.load('BookInfo');
console.log(dataStore.getAttribute('name')); //N
console.log(dataStore.getAttribute('book')); //JSer
//删除
dataStore.removeAttribute('name');
dataStore.removeAttribute('book');
dataStore.save('BookInfo');

23.3.3 Web存储机制

Web Storage

1 Storage

clear()getItem(name)key(index)removeItem(name)setItem(name,value)

2 sessionStorage对象

会话存储,关闭页面后消失

//使用方法存储数据
sessionStorage.setItem('name', 'N');
//使用属性存储数据
sessionStorage.book = 'JSer';
//IE8 only
sessionStorage.begin();
sessionStorage.book = 'JSer';
sessionStorage.commit();
//使用方法读取数据
var name = sessionStorage.getItem('name');
//使用属性读取数据
var book = sessionStorage.book;

for(var i = 0, len = sessionStorage.length; i < len; i++) {
    var value = sessionStorage.getItem(key);
    console.log(key + '=' + value);
}

//使用delete删除一个值
delete sessionStorage.name;
//使用方法删除一个值
sessionStorage.removeItem('book');

3 globalStorage对象

4 localStorage对象

本地存储,要访问同一个localStorage对象,页面必须来自同一个域名(子域名无效),使用同一种协议,在同一个端口

//使用方法存储数据
localStorage.setItem('name', 'N');
//使用属性存储数据
localStorage.book = 'JSer';
//使用方法读取数据
var name = localStorage.getItem('name');
//使用属性读取数据
var book = localStorage.book;

function getLocalStorage() {
    if(typeof localStorage == 'object') {
        return localStorage;
    } else if(typeof globalStorage == 'object') {
        return globalStorage[location.host];
    } else {
        throw new Error('local storage not available');
    }
}
var storage = getLocalStorage();

5 storage事件

EventUtil.addHandler(document, 'storage', function(event) {
    console.log('storage changed for' + event.domain);
});

6 限制

每个来源大小有限制

23.3.4 IndexDB

var indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;

1 数据库

var request, database;
request = indexedDB.open('admin');
request.onerror = function(event) {
    console.log('something bad happend while trying to open:' + event.target.errorCode);
};
request.onsuccess = function(event) {
    database = event.target.result;
};
//设置版本号
if(database.version != '1.0') {
    request = database.setVersion('1.0');
    request.onerror = function(event) {
        console.log('set version error:' + event.target.errorCode);
    };
    request.onsuccess = function(event) {
        console.log('complete.Database name:' + database.name + ',version:' + database.version);
    };
} else {
    console.log('Database already initialized.Database name:' + database.name + ',version:' + database.version)
}

2 对象存储空间

var user = {
    username: '007',
    firstName: 'James',
    lastName: 'Bond',
    password: 'foo'
};
//示例
var store = db.createObjectStore('users', {
    keyPath: 'username'
});
//users中保存着一批用户数据
var i = 0,
    request,
    requests = [],
    len = users.length;
while(i < len) {
    request = store.add(users[i++]);
    request.onerror = function() {
        //处理错误
    };
    request.onsuccess = function() {
        //处理成功
    };
    requests.push(request);
};

3 事务

//db=database
var transaction = db.transaction();
//传入要访问的对象存储空间
var transaction = db.transaction('users');
var transaction = db.transaction(['users', 'anotherStore']);

//设定访问模式,读写什么的
var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;
var transaction = db.transaction('users', IDBTransaction.READ_WRITE);

var request = db.transaction('users').objectStore('users').get('007');
request.onerror = function(event) {
    console.log('did not get the object');
};
request.onsuccess = function(event) {
    var result = event.target.result;
    console.log(result.firstName); //James
};

transaction.onerror = function(event) {
    //整个事务都被取消了
};
transaction.oncomplete = function(event) {
    //整个事务都成功完成了
};

4 使用游标查询

需要检索多个对象的情况下,则需要在事务内部创建游标。

游标就是一指向结果集的指针。

var store = db.transaction('users').objectStore('users'),
    request = store.openCursor();
request.onerror = function(event) {
    //处理失败
};
request.onsuccess = function(event) {
    //处理成功
};

//检索某个结果的信息
request.onsuccess = function(event) {
    var cursor = event.target.result;
    if(cursor) {
        console.log('key:' + cursor.key + ',value:' + JSON.stringify(cursor.value));
    }
};

request.onsuccess = function(event) {
    var cursor = event.target.result,
        value, updateRequest;
    if(cursor) {
        if(cursor.key == 'foo') {
            value = cursor.value; //取得当前的值
            value.password = 'npw'; //更新新值

            updateRequest = cursor.update(value); //请求保存更新
            updateRequest.onsuccess = function() {
                //处理成功
            };
            updateRequest.onerror = function(event) {
                //处理失败
            };
        }
    }
};

request.onsuccess = function(event) {
    var cursor = event.target.result,
        value, deleteRequest;
    if(cursor) {
        if(cursor.key == 'foo') {
            deleteRequest = cursor.delete(value); //请求删除当前项
            deleteRequest.onsuccess = function() {
                //处理成功
            };
            deleteRequest.onerror = function(event) {
                //处理失败
            };
        }
    }
};

request.onsuccess = function(event) {
    var cursor = event.target.result;
    if(cursor) {
        console.log('key:' + cursor.key + ',value:' + JSON.stringify(cursor.value));
        cursor.continue(); //移动到下一项
    } else {
        console.log('done');
    }
};

5 键范围

var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
var store = db.transaction('users').objectStore('users'),
    range = IDBKeyRange.bound('007', 'ace'), //输出的对象的键为007到ace
    request = store.openCursor(range);
request.onsuccess = function(event) {
    var cursor = event.target.result;
    if(cursor) {
        console.log('key:' + cursor.key + ',value:' + JSON.stringify(cursor.value));
        cursor.continue(); //移动到下一项
    } else {
        console.log('done');
    }
};

6 设定游标方向

var IDBCursor = window.IDBCursor || window.webkitIDBCursor;
var store = db.transaction('users').objectStore('users'),
    request = store.openCursor(null, IDBCursor.NEXT_NO_DUPLICATE);
var store = db.transaction('users').objectStore('users'),
    request = store.openCursor(null, IDBCursor.PREV);

7 索引

var store = db.transaction('users').objectStore('users'),
    index = store.createIndex('username', 'username', {
        unique: false
    }); //索引的名字,索引的属性的名字,键再所有纪录中是否唯一
//取得索引
var store = db.transaction('users').objectStore('users'),
    index = store.index('username');

var store = db.transaction('users').objectStore('users'),
    request = store.index('username'),
    request = index.openCursor();
request.onsuccess = function(event) {
    //处理成功
};

var store = db.transaction('users').objectStore('users'),
    request = store.index('username'),
    request = index.openKeyCursor();
request.onsuccess = function(event) {
    //处理成功
    //event.result.key中保存索引键,而event.result.value中保存主键
};
var store = db.transaction('users').objectStore('users'),
    request = store.index('username'),
    request = index.get('007');
request.onsuccess = function(event) {
    //处理成功
};
request.onerror = function(event) {
    //处理失败
};

var store = db.transaction('users').objectStore('users'),
    request = store.index('username'),
    request = index.getKey('007');
request.onsuccess = function(event) {
    //处理成功
    //event.result.key中保存索引键,而event.result.value中保存主键
};
var store = db.transaction('users').objectStore('users'),
    indexNames = store.indexNames,
    index,
    i = 0,
    len = indexNames.length;
while(i < len) {
    index = store.index(indexNames[i++]);
    console.log('index name:' + index.name + ',keypath:' + index.keyPath + ',unique:' + index.unique);
}

var store = db.transaction('users').objectStore('users');
store.deleteIndex('username');

8 并发问题

var request, database;
request = indexedDB.open('admin');
request.onsuccess = function(event) {
    database = event.target.result;
    database.onversionchange = function() {
        database.close();
    };
};

var request = database.setVersion('2.0');
request.onblocked = function() {
    console.log('please close all tabs and try again');
};
request.onsuccess = function() {
    //处理成功,继续
};

9 限制

同源限制;

大小限制。
#24 最佳实践

24.1 可维护性

24.1.1 什么时可维护性的代码

可理解性、直观性、可适应性、可扩展性、可调试性

24.1.2 代码规定

可读性、变量和函数命名、变量类型透明

24.1.3 松散耦合

解耦html/js,解耦css/js,解耦应用逻辑/事件处理程序

24.1.4 编程实践

尊重对象所有权、避免全局量、避免与null进行比较、使用常量

24.2 性能

24.2.1 注意作用域

1 避免全局查找

document 保存起来

2 避免with语句

用局部变量代替

24.2.2 选择正确方法

1 避免不必要的属性查找

存起来

2 优化循环

3 展开循环

消除循环

duff装置

4 避免双重解释

5 其他

使用原生方法;switch和if的比较;位运算。

24.2.3 最小化语句数

1 减少变量声明

var a,b,c;

2 插入迭代量

a[i++];

3 使用数组和对象字面量

24.2.4 优化DOM交互

1 最小化现场更新

将变化存起来在添加到DOM

2 使用innerHTML

3 使用事件代理

4 注意HTMLCollection

24.3 部署

24.3.1 构建过程

24.3.2 验证

24.3.3 压缩

#25 新兴的API

25.1 requestAnimationFrame()

25.1.1 早期动画循环

(function() {
    function updateAnimations() {
        doAnimation1();
        doAnimation2();
    }
    setInterval(updateAnimations, 100);
})();

25.1.2 循环间隔的问题

浏览器计时器的精度问题

25.1.3 mozRequestAnimationFrame

function updateProgress() {
    var div = document.getElementById('status');
    div.style.width = (parseInt(div.style.width, 10) + 5) + '%';
    if(div.style.left != '100%') {
        mozRequestAnimationFrame(updateProgress);
    }
}
mozRequestAnimationFrame(updateProgress);

25.1.4 其他浏览器

(function() {
    function draw(timestamp) {
        //计算两次重绘的时间间隔
        var diff = timestamp - startTime;
        //使用diff确定下一步的绘制时间
        //把startTime重写为这一次的绘制时间
        startTime = timestamp;
        //重绘UI
        mozRequestAnimationFrame(draw);
    }
    var requestAnimationFrame = window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame,
        startTime = window.mozAnimationStartTime || Date.now();
    mozRequestAnimationFrame(draw);
})();

25.2 Page Visibility API

function isHiddenSupported() {
    return typeof(document.hidden || document.msHidden || document.webkitHidden) != 'undefined';
}

if(document.hidden || document.msHidden || document.webkitHidden) {
    //页面隐藏了
} else {
    //页面未隐藏
}

function handleVisibilityChange() {
    var output = document.getElementById('output'),
        msg;
    if(document.hidden || document.msHidden || document.webkitHidden) {
        msg = 'page is now hidden.' + (new Date()) + '<br>';
    } else {
        msg = 'page is now visible.' + (new Date()) + '<br>';
    }
    output.innerHTML += msg;
}
EventUtil.addHandler(document, 'msvisibilitychange', handleVisibilityChange);
EventUtil.addHandler(document, 'webkitvisibilitychange', handleVisibilityChange);

25.3 Geolocation API

navigator.geolocation.getCurrentPosition(function(position) {
    drawMapCenteredAt(position.coords.latitude, position.coords.longitude);
}, function(error) {
    console.log('error code:' + error.code);
    console.log('error msg:' + error.message);
}, {
    enableHighAccuracy: true, //表示尽可能使用最准确的位置信息
    timeout: 5000, //等待位置信息的最长时间
    maximumAge: 25000 //有效时间

});
var watchId = navigator.geolocation.watchPosition(function(position) {
    drawMapCenteredAt(position.coords.latitude, position.coords.longitude);
}, function(error) {
    console.log('error code:' + error.code);
    console.log('error msg:' + error.message);
});
clearWatch(watchId);

25.4 File API

var filesList = document.getElementById('files-list');
EventUtil.addHandler(filesList, 'change', function(event) {
    var files = EventUtil.getTarget(event).files,
        i = 0,
        len = files.length;
    while(i < len) {
        console.log(files[i].name + '(' + files[i].type + ',' + files[i].size + 'bytes)');
        i++;
    }
});

25.4.1 FileReader类型

方法 简介
readAsText(file,encoding) 以纯文本形式读取文件,将读取到的文本保存在result属性中
readAsDataURL(file) 读取文件并将文件以数据URL的形式保存在result中
readAsBinaryString(file) 读取文件并将一个字符串保存在result属性中
readAsArrayBuffer(file) 读取文件并将一个包含文件内容的ArrayBuffer保存在result属性中
var filesList = document.getElementById('files-list');
EventUtil.addHandler(filesList, 'change', function(event) {
    var info = '',
        output = document.getElementById('output'),
        progress = document.getElementById('progress'),
        files = EventUtil.getTarget(event).files,
        type = 'default',
        reader = new FileReader();
    if(/image/.test(files[0].type)) {
        reader.readAsDataURL(files[0]);
        type = 'image';
    } else {
        reader.readAsText(files[0]);
        type = 'text';
    }
    reader.onerror = function() {
        output.innerHTML = 'could not read file,error code is' + reader.error.code;
    };
    reader.onprogress = function(event) {
        if(event.lengthComputable) {
            progress.innerHTML = event.loaded + '/' + event.total;
        }
    };
    reader.onload = function() {
        var html = '';
        switch(type) {
            case 'image':
                html = "<img src=\'" + reader.result + "\'>";
                break;
            case 'text':
                html = reader.result;
                break;
            default:
                break;
        }
        output.innerHTML = html;
    };
});

25.4.2 读取部分内容

function blobSlice(blob, startByte, length) {
    if(blob.slice) {
        return blob.slice(startByte, length);
    } else if(blob.webkitSlice) {
        return blob.webkitSlice(startByte, length);
    } else if(blob.mozSlice) {
        return blob.mozSlice(startByte, length);
    } else {
        return null;
    }
}
var filesList = document.getElementById('files-list');
EventUtil.addHandler(filesList, 'change', function(event) {
    var info = '',
        output = document.getElementById('output'),
        progress = document.getElementById('progress'),
        files = EventUtil.getTarget(event).files,
        reader = new FileReader(),
        blob = blobSlice(files[0], 0, 32); //32B
    if(blob) {
        reader.readAsText(blob);
        reader.onerror = function() {
            output.innerHTML = 'could not read file,error code is' + reader.error.code;
        };
        reader.onload = function() {
            output.innerHTML = reader.result;
        };
    } else {
        console.log("your browser doesn't support slice().");
    }
});

25.4.3 对象URL

function creatObjectUrl(blob) {
    if(window.URL) {
        return window.URL.createObjectURL(blob);
    } else if(window.webkitURL) {
        return window.webkitURL.createObjectURL(blob);
    } else {
        return null;
    }
}

var filesList = document.getElementById('files-list');
EventUtil.addHandler(filesList, 'change', function(event) {
    var info = '',
        output = document.getElementById('output'),
        progress = document.getElementById('progress'),
        files = EventUtil.getTarget(event).files,
        reader = new FileReader(),
        url = creatObjectUrl(files[0]);
    if(url) {
        if(/image/.test(files[0].type)) {
            output.innerHTML = "<img src=\'" + url + "\'>";
        } else {
            output.innerHTML = 'not an img';
        }
    } else {
        output.innerHTML = 'your browser dose not support object URLs.';
    }
});
function revokeObjectURL(url) {
    if(window.URL) {
        window.URL.revokeObjectURL(url);
    } else if(webkitURL) {
        window.webkitURL.revokeObjectURL(url);
    }
}

25.4.4 读取拖放的文件

var droptarget = document.getElementById('droptarget');

function handleEvent(event) {
    var info = '',
        output = document.getElementById('output'),
        files, i, len;
    EventUtil.preventDefault(event);
    if(event.type == 'drop') {
        files = event.dataTransfer.files;
        i = 0;
        len = files.length;
        while(i < len) {
            info += files[i].name + '(' + files[i].type + ',' + files[i].size + 'bytes)<br>';
            i++;
        }
        output.innerHTML = info;
    }
}
EventUtil.addHandler(droptarget, 'dragenter', handleEvent);
EventUtil.addHandler(droptarget, 'dragover', handleEvent);
EventUtil.addHandler(droptarget, 'drop', handleEvent);

25.4.5 使用XHR上传文件

var droptarget = document.getElementById('droptarget');

function handleEvent(event) {
    var info = '',
        output = document.getElementById('output'),
        data, xhr,
        files, i, len;
    EventUtil.preventDefault(event);
    if(event.type == 'drop') {
        data = new FormData();
        files = event.dataTransfer.files;
        i = 0;
        len = files.length;
        while(i < len) {
            data.append('file' + i, files[i]);
            i++;
        }
        xhr = new XMLHttpRequest();
        xhr.open('post', 'exp.php', true);
        xhr.onreadystatechange = function() {
            if(xhr.readyState == 4) {
                console.log(xhr.responseText);
            }
        };
        xhr.send(data);
    }
}
EventUtil.addHandler(droptarget, 'dragenter', handleEvent);
EventUtil.addHandler(droptarget, 'dragover', handleEvent);
EventUtil.addHandler(droptarget, 'drop', handleEvent);

25.5 Web计时

window.performance对象

25.6 Web Workers

让js在后台运行

25.6.1 使用Worker

var worker = new Worker('stufftodo.js');
worker.postMessage('start'); //给worker传递消息
worker.postMessage({
    type: 'command',
    message: 'start'
});
//worker返回的数据
worker.onmessage = function(event) {
    var data = event.data;
    //对数据进行处理
}
worker.onerror = function(event) {
    console.log('error:' + event.filename + '(' + event.lineno + ')' + event.message);
}
worker.terminate(); //立即停止worker的工作

25.6.2 Worker全局作用域

Web Workers本身也是个最小化的运行环境

包含:

最小化的navigator对象,包括onLineappNameappVersionuserAgentplatform属性;

只读的location对象;

setTimeout()setInterval()clearTimeout()clearInterval()方法;

XMLHttpRequest构造函数。

//web worker内部的代码
self.onmessage = function(event) {
    var data = evnet.data;
    //处理数据
    data.sort(function(a, b) {
        return a - b;
    });
    self.postMessage(data);
};

//在页面中
var data = [23, 23, 434, 5346, 213, 52],
    worker = new Worker('exp.js');
worker.onmessage = function(event) {
    var data = event.data;
    //对排序后的数组进行操作
};
//将数组发送给worker排序
worker.postMessage(data);
//web worker内部的代码
self.close(data);

25.6.3 包含其他脚本

//web worker内部的代码
importScripts('f1.js', 'f2.js');

小程序采坑

小程序采坑

不定期更新

异步优化

function wxGetImageInfo({ src }){
  return new Promise((resolve, reject) => wx.getImageInfo({
    src: src,
    success: resolve,
    fail: reject
  }))
}
function wxGetSystemInfo() {
  return new Promise((resolve, reject) => wx.getSystemInfo({
    success: resolve,
    fail: reject
  }))
}
module.exports = {
  wxGetImageInfo,
  wxGetSystemInfo
}
//获取设备宽高来设置canvas宽高,接着获取一张网络图片,获取图片信息绘制到画布,接着写几个字
util.wxGetSystemInfo().then((res) => {
      this.data.canvasWidth = res.windowWidth
      this.data.canvasHeight = res.windowHeight
      rpx = res.windowWidth / 750 //750为设计稿宽度
    }).then(() => {
      this.setData({
        canvasWidth: this.data.canvasWidth,
        canvasHeight: this.data.canvasHeight
      })
    }).then(() => {
      return Promise.resolve(
        util.wxGetImageInfo({
          src: 'http://wx4.sinaimg.cn/mw690/6c7bfb12gy1ftui8gu8uaj20j60j1gmt.jpg'
        }).then((res) => {
          this.setData({
            imgUrl: res.path
          })
          var path = res.path; //这是得到文件的临时路径
          //然后将图片画在背景图上
          ctx.drawImage('./image/bg.jpg', 0, 0, this.data.canvasWidth, this.data.canvasHeight * .8);
        })
      )

    }).then(() => {
      ctx.fillStyle = 'rgb(255, 0, 0)';
      ctx.font = "normal bold 50px 黑体";
      ctx.fillText("开心", 50, 50);
      ctx.draw()
    })

web-view

流程

<!-- html -->
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script>

// javascript
$(function(){//dom ready
    wx.miniProgram.getEnv(function(res) {//WeixinJSBridgeReady
        //操作写这
        
    })
})

通信

小程序端通过url传参给网页

//小程序端通过url传参给网页
<web-view src="https://mp.weixin.qq.com?id=9"></web-view>


<script>   
//网页端取参
function getUrlParam(name) {
	var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象

	var r = window.location.search.substr(1).match(reg); //匹配目标参数

	if (r != null) return decodeURIComponent(r[2]);//注意解码
	return null; //返回参数值
}
    getUrlParam(id)
</script>

网页端通过postMessage 给小程序传数据

//小程序端通过bindmessage接收数据
<web-view src="https://mp.weixin.qq.com?id=9"  bindmessage='webMsg'></web-view>
<script>  
Page({
  webMsg(e) {
   console.log(e)
  }
})
</script>

<script>   
//网页端取参
wx.miniProgram.navigateBack({delta: 1})//特定时机(小程序后退、组件销毁、分享)才能触发发送数据
wx.miniProgram.postMessage({ data: 'foo' })
</script>

canvas

绘制要调用 ctx.draw(),多次绘制叠加的话要加参数ctx.draw(true)

《css揭秘》半透明边框及多层边框的实现

写了一个多小时居然出错丢失了,混蛋啊。。。

《css揭秘》现学现卖系列

半透明边框

1.图片方案

没啥讲的。

我就是要用css实现。

2.border+background-clip方案

很天真,想着直接border-color给个rgba值不就完了嘛,然后

img

貌似跟想象中的效果不太一样么。还是自己知识水平不够,不知道border是盖在background-color,加padding和margin也没用。额,那有啥办法么,有的~

b2.png

background-clip属性登场,她的值跟box-sizing差不多,这里给了个padding-box。

小结,兼容性IE9+(RGBA),后退平稳(先设个正常的border颜色)。

3.outline方案

虽然没怎么见人用过,但莫名感觉很有用的属性。

img

这也是我在项目中使用的方法,

兼容性嘛,还是RGBA的IE9+,后退平稳。

4.box-shadow方案

box-shadow用的人就很多了,但好多人没留意过她的语法

box-shadow: h-shadow v-shadow blur spread color inset;

如果就第三项blur模糊距离给0,第四项阴影尺寸给值,那么

img

实现啦~

不过,有点小问题,在IE9以下,就没边了,所以我没用她,不过把她放最后是为了什么呢,嘿嘿,就是为了引出本文的第二部分。

多层边框

图片方法就不说了,border只能一层(或许径向渐变能整一下?好像不行),outline+border有两层,也算多边框了,那box-shadow呢,呵,想加几个就几个~

img

box-shadow:0px 0px 0px 5px #888888 inset,0px 0px 0px 5px #000000, 0px 0px 0px 10px #ff0000;

这里有三层边框,一个5px的内部阴影(inset),一个5px的黑框,一个5px(10-5,前面的阴影盖在后来的上面)的红框。

虽然是IE9+,但还算可接受啦。

对了,宽度要注意!其他没什么了。

就这样吧。

2016年6月25日补充:

box-shadow方案与outline方案相比,支持圆角。不过outline以后可能会支持圆角

移动端网页开发采坑

移动端网页开发采坑

注意,JS代码片段基本都是从vue项目里翻出来的

不定期更新

webview

通信

//url上取参
  created() {
    this.model.DeviceNo = decodeURIComponent(this.$route.query["no"]);
  }

//调用原生代码暴露的接口
,appFun(state, { funName, val = null }) {
      if (state.dev.type == 2) {
        window.android[funName](val);
      } else if (state.dev.type == 1) {
        window.webkit.messageHandlers[funName].postMessage(val);
      }
    }
 
	// 把函数挂在window对象上来暴露给APP
    let _this = this;   
    window.setGeolocation = function(params) {
      _this.setGeolocation(params);
    };

或者

引用jsbridge

权限

安卓端不能网页下载

<a href='..' download>

安卓端需要设置拦截,触发系统下载

IOS端卡死

可能是因为网页定位什么的权限 , IOS端在项目配置里要注册相应权限

微信

音频自动播放

document.addEventListener("WeixinJSBridgeReady", function() {
				document.getElementById('music_bg').play();//自动播放
				document.getElementById('music_open').load();//先开始缓存,之后可直接play()
			}, false);
//或者在wx.ready()事件里执行

IOS页面返回强刷

window.onpageshow = function(event) {
	if (event.persisted) {
		window.location.reload()
	}
};

ios微信6.7.4调起输入框后导致页面高度异常

$('textarea,input').on('blur',function(){
    setTimeout(function(){
        window.scrollTo(0, 0)
    },100)
})

杂项

IOS点击事件不生效

css设置cursor: pointer;

占满高度

html,
body {
	height: 100%
}

IOS滚动卡断

html {
	overflow-y: scroll;
	/* 0 */
	-webkit-overflow-scrolling: touch;
	/* 0 */
}

横屏提醒

(function rotate() {
	var orientation = window.orientation;
	var pd = null;

	function createPd() {
		if (document.getElementById('preventTran') === null) {
			var imgData =
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAABaCAYAAADkUTU1AAAI9ElEQVR4Xu1cfXBcVRU/5+Z1N8GEj2AhFQvUIigfBetYaRVbBhADU2wHVoYk3bx3k8kMcSyFPxzUf8IfOjrqIHYUXbL3vW6mKXbtINapg1ColLEUnYIj9QPGOE0VdUjjlE3tdnffO87J7GY26yZ9H5tNst37X5tzzu/87rl777v3nnMR5rhFo9HLhBDrhRC3AMBqAFgBABfmYU8CwAgAHAGAVwDgJaXUO+Vc6u7uXhkOh0/GYrGxIC5jEOVZdLG3t7fdcZyHiOgORHSL4xDRfiHEE/F4fB8AEGNIKdcS0fMA8IxpmluC+OzWEdcY0Wh0jaZp2wFgjWulMoJE9CoRbRVCEHcCIp4PAOOpVOqSZDJp+7VdMcIbNmzQVqxYMYCIXwEA4dehEj2O+GlEfF/h/xFxfTwef9mv/YoQ7u/vb06n00kA+FypIxweAHgdAJ4DgF9nMpmj4+Pj77Jca2vr0nA4fC0ArAeAO4lotYvh/22l1JfnjXAkEmluaWn5JQB8ukx09hLRgGVZb7hxUNf1m4QQjxHRxlmI/0kpxZ3kqwWNMEopfwIAkRL0fwNAn1Lq51696ujouKKxsfEwAFw6k246nV45PDzMs7vnFoiwlPIRAPhuCeqbjuPcYVnWv7x609nZ+cFwOMzL0xVn0d2qlOKJ0XPzTZjXxYaGhqMAEC5C/aOmaetisRivr55aV1fXsiVLlhxExJVnU+QlyjTNz55NrtzffROWUj4DAJuKjI4j4up4PH7MjyOGYTyNiPe70SWiDCK+XymVciNfLOOLcDQaXaVpGk9EU/qO40Qtyxry6kBB3jCMpUQUEUJsIKIbEPEqANBmsseypmn+1CueL8JSyh8AQH8BjIiOmKb5ca/gs8l3dnae39jYeJfjODxjXw8APNSn1mMiUqZp9njF9EXYMIw3EfG6IsKbTNN81iu4F/mBgQExOjq6DgA2A8AnAeC3SqmHvdhgWb+E/4mIbXkwO5VKXZxMJj1PVF6drYS8X8IPI+K3AKCBiLabprmtEs5Uw4YvwuyYrusXnjlzRtu1a1eg7Vo1SAaepavtZCXxfEe4kk5U01adcDV7ez6w6hGej16vJmY9wtXs7fnAKhvhSCTS1NTUtFQIcZ5t2xUbBYjo+7TRbecIITKZTObk8PDwf8rpTCPT0dFxUTgc/ioA8Kdjg1uQhShHRG8T0bZTp069kEwmMwUfpwgbhnEtIv4GAC5YiAT8+sTEbdu+NZFI/GNqtxSJRFqbm5v/ioiFKxC/9heq3gki+qhpmu9ORrinp+cpIupdqN5WyK+fKaU2Y19f3wW5XO4Eb/XKGHYK9zteQIlIuDhQ92KyIrKO41yNhmF0IWLZsygi6jdN88mKoM2BEcMwHkTEH7o1TUSP8EH64wBQdgNfa4QBwCrcHHyhXC/VIOE9TJiPOu+tE+bZqsZ+wwBQj/C0kV2PsNv5v0pyXpel+pAuDUytDulfAMDd59KyVCdciPYiHdJj2Wx2zdDQ0N90Xf+wEILzRS7Kc5pch2spwg4iLo3H4+OFoEkpPwAAf8/flNYc4f1KqdtL5yMpJSfKfKqwLNVShA8rpW4uJdzT0/M6Ed1Uc4Q56w8RP6OU4ohOtu7u7tuEEM/nDyRqbkgzxywRDRLRbkTsRES9KDmmJgnP9mG7h494ONz/90NnrUW6LM1OWErJidd1wvUIV2nL5wXG7/awPqQX+bf0bIMkyd/S50yEiWi4Trh4PNTaOlyIMGfB3nMunHgQUYy/tL6RrzUqxzlJRFMf4l6WjErJIiJXajXPYG8NIm50izV5mabr+i1CCN+FT27BFoJcLpe7hi/EeeI6lE+6Xgh+zZUPu5VS909mAESj0as1TePqsfPmCm0+7RLRO7Ztr0okEiemklrypLlc7sr5dG4OsF8TQtwzODjIxWPTSwA4P6ulpYWrSh5DxE/MAXi1THKqBpcHfjOVSh0qrkadMelMStmSTqdbGxsbF1W+Vi6XOyOEOGFZVrpc71Ysy65aoQuKUycctAcXun49wgs9QkH9W5QR3rJly/VNTU0jsVjsv147YFERbm9vDy9btoxvA28koveI6POWZR3wQtoP4YLO5Bsb1Wy6rm8UQhSX2T+tlHrAiw+eCRuGsQcRbwOAo1xGK4T4VSaTeXFoaOiUF2A/slJKTpHkVMnJRkRPmqY5VdbrxqYfwuX2z1kA4Az0P/DzMgCwzzTN424c8CIjpdxd/MCC4zjbLMt6wosNz4R1Xb9ZCMHbydkaX+TxmzpcZ/xjpRSXzwdqfX19S3K5HG8ACrf5IIRYOzg4+KoXw54Jc+HysWPHuH74EpdA25VSW13Kziim6zqXy3OEC20slUq1eX2mxjNhRpNSmlxR64LEHk3THojFYjzkAzUp5e8AoLjs/kdKqQe9GvVLmNON+cGS2dpzjuNsmmnX4sVRXdc7hBA7i3R4hfiYUur3XuywrC/C/CBBOBzm93RC5QCJ6MWxsbGNe/fu9fxhUGovGo1e3tDQcAQRLy78jYieNU2z+EkN17x9Ec4P6xcAgJenaY2IDk5MTNyVTCYnXHsxgyB3bCgUehkRbywim7Ft+4ZEIvGWH/u+Ceu6/pAQ4ntlQF87ffr03UFL5Xt7ey+1bXsfP4ZSjOE4zqOWZfH7A76ab8JdXV1XhUKht2cY0qOO48gdO3bs9+OVYRh3AkAcES8r0edSHM7e5yMcX8034fyw/jMAXAMAXFNYehTETvFE83Wl1F/ceNfd3X2dEOJr+Sdqpj1CRkSHJyYmbg/6UwlE2DAMPuyLZLPZezVNiyFi6ZtazJOJ8+0F54Mdymazbx0/fnwyU2758uWtoVDoI7Ztr+WTRSJaW67eiSfBTCazeefOne+56bjZZAIRzhtmG8Q7mba2tu8AwBcrWKTFnfX4yMjIowcOHMgFJcv6lSA8zQ8p5a0AwJPZqiAOEtEb/AigZVkHg9gp1a04YQaIRCINzc3N9yHil4honYeIF4b/9/Pf374np5k6aU4IF4NJKT8EAO355E5+NelyACjcBvJ7WKMAwLusV3K53L5EIsH/nrP2PzAJNfmP9znfAAAAAElFTkSuQmCC';
			pd = document.createElement('div');
			pd.setAttribute('id', 'preventTran');
			pd.style.position = 'fixed';
			pd.style.left = '0';
			pd.style.top = '0';
			pd.style.width = '100%';
			pd.style.height = '100%';
			pd.style.overflow = 'hidden';
			pd.style.backgroundColor = '#2e2e2e';
			pd.style.textAlign = 'center';
			pd.style.zIndex = '99999';
			document.getElementsByTagName('body')[0].appendChild(pd);
			var img = document.createElement('img');
			img.src = imgData;
			pd.appendChild(img);
			img.style.margin = '60px auto 30px'
			var br = document.createElement('br');
			var p = document.createElement('p');
			p.style.width = '100%';
			p.style.height = 'auto';
			p.style.fontSize = '22px';
			p.style.color = '#626262';
			p.style.lineHeight = '34px';
			p.style.textAlign = 'center';
			p.innerHTML = '为了您的良好体验';
			p.appendChild(br);
			p.innerHTML += '请将手机/平板竖屏操作';
			pd.appendChild(p);
		}
	}
	if (orientation == 90 || orientation == -90) {
		if (pd == null && document.getElementById('preventTran') === null) createPd();
		document.getElementById('preventTran').style.display = 'block';
	}
	window.onorientationchange = function() {
		if (pd == null && document.getElementById('preventTran') == null) createPd();
		document.getElementById('preventTran').style.display = 'none';
		rotate();
	};
})();

rem布局

//设计稿为750px,100px=1rem
(function(doc, win) {
				var docEl = doc.documentElement,
					resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
					recalc = function() {
						var clientWidth = docEl.clientWidth;
						if(!clientWidth) return;
						if(clientWidth >= 750) {
							docEl.style.fontSize = '100px';
						} else {
							docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
						}

					};
				if(!doc.addEventListener) return;
				win.addEventListener(resizeEvt, recalc, false);
				doc.addEventListener('DOMContentLoaded', recalc, false);
			})(document, window);

纯爱我老婆

QQ音乐API整理

QQ音乐API整理

blog

最近准备用vue做个音乐播放器,网上找了找音乐API,看了一圈,还是QQ音乐最合适,这里做个整理

歌曲搜索

接口地址

var num = 3,
    name = '王菲',
    urlString = `http://s.music.qq.com/fcgi-bin/music_search_new_platform?t=0&n=${num}&aggr=1&cr=1&loginUin=0&format=json&inCharset=GB2312&outCharset=utf-8&notice=0&platform=jqminiframe.json&needNewCode=0&p=1&catZhida=0&remoteplace=sizer.newclient.next_song&w=${name}`;

调用

//用了个PHP代理解决跨域问题
$.post("proxy.php", {
        urlString
      }, function(data) {
        data = JSON.parse(data)
        data['data']['song']['list'].forEach(
          e => console.log(e['f'])
        )
      })
<?php
$url=$_POST['urlString'];
$res = file_get_contents($url);
echo $res;
?>

注释

num:请求搜索的数量

name:搜索的关键词

歌曲榜单

接口地址

// 新歌榜:http://music.qq.com/musicbox/shop/v3/data/hit/hit_newsong.js
// 总榜:http://music.qq.com/musicbox/shop/v3/data/hit/hit_all.js

调用

$.ajax({
  type: "get",
  async: false,
  url: "http://music.qq.com/musicbox/shop/v3/data/hit/hit_newsong.js",
  dataType: "jsonp",
  jsonp: "callback",
  jsonpCallback: "JsonCallback",
  scriptCharset: 'GBK',//设置编码,否则会乱码
  success: function(data) {
    console.log(JSON.stringify(data))
  },
  error: function() {
    alert('fail');
  }
});

注释

主要获取的是id(歌曲id)、albumId(图片id)

歌曲地址

接口地址

var id = 101369814,
    src = `http://ws.stream.qqmusic.qq.com/${id}.m4a?fromtag=46`

调用

<audio src="http://ws.stream.qqmusic.qq.com/101369814.m4a?fromtag=46" controls></audio>

注释

没啥好说的。。

歌词地址

接口地址

var id = 101369814,
    txt = `http://music.qq.com/miniportal/static/lyric/${id%100}/${id}.xml`;

调用

//用了个PHP代理解决跨域问题
$.post("proxy.php", {
        txt
      }, function(data) {
        console.log(data)
      })
<?php
$url=$_POST['txt'];
$res = file_get_contents($url);
$s = iconv('gbk','UTF-8',$res);//大坑,一是转编码,二是不能直接iconv输出,得有个变量转接
echo $s;
?>

注释

没啥说的

歌曲图片

接口地址

var image_id = 140820,
    width = 300,
    pic = `http://imgcache.qq.com/music/photo/album_${width}/${image_id%100}/${width}_albumpic_${image_id}_0.jpg`;

调用

<img src="http://imgcache.qq.com/music/photo/album_300/20/300_albumpic_140820_0.jpg" alt="">

注释

image_id:图片id

width:图片尺寸

《javascript高级程序设计》笔记4(第8章 BOM,第9章 客户端检测,第10章DOM,第11章DOM扩展)

第八章 BOM

BOM:浏览器对象模型

8.1 window对象

8.1.1 全局作用域

所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法。

全局变量不能通过delete删除,而直接在window对象上定义的属性可以。

//全局作用域

var age = 29;

window.color = 'red';

delete window.age; //false

delete window.color; //return true

alert(window.age); //29

alert(window.color); //undefined;

8.1.2窗口关系及框架

window.frames[0];//第一个框架

top.frames[0];//最外层框架

8.1.3 窗口位置

跨浏览器取得窗口左边和上边的位置

var leftPos = (typeof window.screenLeft == "number") ? window.screenLeft : window.screenX; //除ff外都支持screenLeft;
var topPos = (typeof window.screenTop == "number") ? window.screenTop : window.screenY;
window.moveTo(x,y);//移动至
window.moveBy(x,y);//移动

左上角为零点。右、下为正

8.1.4窗口大小

//视口大小
var pageWidth = window.innerWidth;
var pageHeight = window.innerHeight;
if (typeof(pageWidth) != "number") {
    if (document.compatMode == "CSS1Compat") {
        pageWidth = document.documentElement.clientWidth;
        pageHeight = document.documentElement.clientHeight;
    } else {
        pageWidth = document.body.clientWidth; //IE6混杂模式
        pageHeight = document.body.clientHeight;
    }
} 

移动设备上除IE外

//保存着可见视口
window.innerWidth;
window.innerHeight;
//布局视口,即渲染后页面的实际大小
document.documentElement.clientWidth;
document.documentElement.clientHeight;
window.resizeTo(100,100);//调整大100*100
window.resizeBy(100,50);//调整到200*150
window.resizeTo(300,300);//调整到300*300

8.1.5导航和打开窗口

window.open()导航到一个特定的URL也可以打开一个新的浏览器窗口,可接受四个参数:要加载的URL、窗口目标、一个特性字符串、一个表示新页面是否取代浏览器历史纪录中当前加载页面的布尔值。

window.open('http://www.bilibili.com/','_blank');//等同于<a href="http://www.bilibili.com/" target="_blank"></a>

target可取_self _parent _top _blank

可将window.open()创建的窗口调整大小resizeTo() 调整位置moveTo() 关闭close() 切断联系.opener=null

安全限制:弹还是不弹,这是一个问题

检测弹出窗口是否被屏蔽

var blocked = false;
try {
    var w = window.open("url", '_blank');
    if (w == null) {
        blocked = true;
    }
} catch (e) {
    blocked = true;
}
if (blocked) {
    alert('被阻挡了!');
};

8.1.6间歇调用和超时调用

//超时调用
//一秒后显示个弹窗
//不推荐,传递字符串
setTimeout("alert()", 1000);
//推荐的调用方式
setTimeout(function() {
    alert();
}, 1000);

var timeoutId = setTimeout(function() {
    alert();
}, 1000);
clearTimeout(timeoutId); //取消
//间歇调用
var num = 0;
var max = 10;
var intervalId = null;

function incrementNumber() {
    num++;
    if (num == max) {
        clearInterval(intervalId);
        alert();
    }
}
intervalId = setInterval(incrementNumber, 500); //.5s执行一次
//超时调用模拟间歇调用
var num = 0;
var max = 10;

function incrementNumber() {
    num++;
    if (num < max) {
        setTimeout(incrementNumber, 500);
    } else {
        alert();
    }
}
setTimeout(incrementNumber, 500);

很少使用真正的间歇调用,原因是后一个间歇调用可能在前一个间歇调用结束之前启动

8.1.7系统对话框

aler()

confirm() 返回布尔值

prompt() 生成提示输入框

window.print() 打印

window.find() 查找

8.2localtion对象

属性名 例子 说明
hash '#contents' #后跟的字符串
host 'www.wrox.com:80'
hostname 'www.wrox.com'
href 'http:/www.wrox.com'
prthname '/WileyCDA/' 目录和(或)文件名
port '8080'
protocol 'http:'
search '?q=javascript' 查询字符串,以?开头

8.2.1查询字符串参数

//查询字符串
function getQueryStringArgs() {
    //取得查询字符串并去掉开头的问号
    var qs = (location.search.length > 0 ? location.search.substring(1) : ''),
        //保存数据的对象
        args = {},
        //取得每一项
        items = qs.length ? qs.split("&") : [],
        item = null,
        name = null,
        value = null,
        i = 0,
        len = items.length;
    //逐个将每一项添加到args对象中
    for (i = 0; i < len; i++) {
        item = items[i].split("=");
        name = decodeURIComponent(item[0]);
        value = decodeURIComponent(item[1]);
        if (name.length) {
            args[name] = value;
        }
    }
    return args;
}
//假设查询字符串是?q=js&num=10
var agrs = getQueryStringArgs();
alert(args["q"]); //js
alert(args["num"]); //10

8.2.2位置操作

var w = 'www.wrox.com';
location.assign(w);
window.location = w;
location.href = w;
//效果都一样

每次修改localtion的属性(hash除外),页面都会以新URL重新加载

location.replace();//不会右后退按钮
location.reload();//重新加载,刷新
location.reload(true);//从服务器重新加载,相当于`ctrl+f5`

8.3navigator对象

识别客户端浏览器的事实标准

8.3.1检测插件

//检测插件
//ie中除外
function hasPlugin(name) {
    name = name.toLowerCase();
    for(var i = 0; i < navigator.plugins.length; i++) {
        if(navigator.plugins[i].name.toLowerCase().indexOf(name) > -1) {
            return true;
        }
    }
    return false;
}

alert(hasPlugin("flash"));
alert(hasPlugin('QuickTime'));

//ie
function hasIEPlugin(name) {
    try {
        new ActiveXObject(name);
        return true;
    } catch(e) {
        return false;
    }
}

alert(hasIEPlugin("ShockwaveFlash.ShockwaveFlash"));
alert(hasIEPlugin("QucikTime.QuickTime"));

//通用形式,针对每个插件分别创建检测函数

function hasFlash() {
    var result = hasPlugin("Flash");
    if(!result) {
        result = hasIEPlugin("ShockwaveFlash.ShockwaveFlash");
    }
    return result;
}

function hasQuickTime() {
    var result = hasPlugin("QuickTime");
    if(!result) {
        result = hasIEPlugin("QucikTime.QuickTime");
    }
    return result;
}

8.3.2注册处理程序

RSS什么的,略

8.4screen对象

表面客户端的能力,包括显示器的信息

8.5history对象

//history

history.go(-1);
history.back();
//后退一页
history.go(1);
history.forward();
//前进一页
history.go(2); //前进两页

history.go("w.com"); //跳转到最近的w.com页面

history.length; //如果是第一个打开的页面则为0

第九章 客户端检测

不到万不得已,就不要使用客户端检测,先设计最通用的方案,然后再使用特定于浏览器的技术增强方案。

9.1能力检测

//基本模式
if (object.propertyInQuestion) {
    //使用object.propertyInQuestion
};

不是识别特定的浏览器,而是识别浏览器的能力。

9.1.1更可靠的能力检测

//更可靠的能力检测
//author:Peter Michaux
function isHostMethod(object, property) {
    var t = typeof object[property];
    return t == 'function' ||
        (!!(t == 'object' && object[property])) ||
        t == 'unknown';
}

result = isHostMe thod(xhr, 'open'); //true
result = isHostMethod(xhr, 'foo'); //false

9.2怪癖检测

对有直接影响的怪癖才进行检测,尽量再脚本开始的时候就进行检测。

9.3用户代理检测

可通过navigator.userAgent访问,对服务器端来说是较常用的做法,但再客户端得排在能力检测和怪癖检测之后。

9.3.1用户代理字符串的历史

9.3.2用户代理字符串检测技术

if (isIE6 || isIE7) { //不推荐,没考虑向后兼容   
}
if (ieVer >= 6) {} //可以

识别浏览器

var client = function() {
    var engine = {
        ie: 0,
        gecko: 0,
        webkit: 0,
        khtml: 0,
        opera: 0,
        ver: null
    };
    var browser = {
        ie: 0,
        firefox: 0,
        safari: 0,
        konq: 0,
        opera: 0,
        chrome: 0,
        ver: null
    };
    var system = {
        win: false,
        max: false,
        x11: false,

        //移动设备
        iphone: false,
        ipod: false,
        ipad: false,
        ios: false,
        android: false,
        nokiaN: false,
        winMobile: false,

        //游戏系统
        wii: false,
        ps: false
    };

    //识别呈现引擎及浏览器
    var ua = navigator.userAgent;
    if (window.opera) {
        engine.ver = browser.ver = window.opera.version;
        engine.opera = browser.opera = parseFloat(engine.ver);
    } else if (/AppleWebkit\/(\S+)/.test(ua)) {
        engine.ver = RegExp["$1"];
        engine.webkit = parseFloat(engine.ver);
        //确定是chrome还是safari
        if (/Chrome\/(\S+)/.test(ua)) {
            browser.ver = RegExp['$1'];
            browser.chrome = parseFloat(browser.ver);
        } else if (/Version\/(\S+)/.test(ua)) {
            browser.ver = RegExp['$1'];
            browser.safari = parseFloat(browser.ver);
        } else {
            var safariVersion = 1;
            if (engine.webkit < 100) {
                safariVersion = 1;
            } else if (engine.webkit < 312) {
                safariVersion = 1.2;
            } else if (engine.webkit < 412) {
                safariVersion = 1.3;
            } else {
                safariVersion = 2;
            }
            browser.safari = browser.ver = safariVersion;
        }

    } else if (/KHTML\(\S+)/.test(ua)) {
        engine.ver = browser.ver = RegExp["$1"];
        engine.khtml = browser.knoq = parseFloat(engine.ver);
    } else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {
        engine.ver = RegExp["$1"];
        engine.gecko = parseFloat(engine.ver);

        if (/Firefox\/(\S+)/.test(ua)) {
            browser.ver = RegExp['$1'];
            browser.firefox = parseFloat(browser.ver);
        }
    } else if (/MSIE ([^;]+)/.test(ua)) {
        engine.ver = browser.ver = RegExp["$1"];
        engine.ie = browser.ie = parseFloat(engine.ver);
    }
    //检测浏览器
    browser.ie = engine.ie;
    browser.opera = engine.opera;
    //检测平台
    var p = navigator.platform;
    system.win = p.indexOf("Win") == 0;
    system.mac = p.indexOf("Mac") == 0;
    system.x11 = p.indexOf("X11") == 0 || p.indexOf("Linux") == 0;

    //检测windows操作系统
    if (system.win) {
        if (/Win(?:dows)?([^do]{2})\s?(\d+\.\d+)?/.test(ua)) {
            if (RegExp['$1'] == "NT") {
                switch (RegExp['$2']) {
                    case "5.0":
                        system.win = "2000";
                        break;
                    case "5.1":
                        system.win = "XP";
                        break;
                    case "6.0":
                        system.win = "Vista";
                        break;
                    case "6.1":
                        system.win = "7";
                        break;
                    default:
                        system.win = "NT";
                        break;
                }
            } else if (RegExp['$1'] == "9x") {
                system.win = "ME";
            } else {
                system.win = RegExp['$1'];
            }
        }
    }
    //移动设备
    system.iphone = ua.indexOf('iPhone') > -1;
    system.ipod = ua.indexOf('iPod') > -1;
    system.ipad = ua.indexOf('iPad') > -1;
    system.nokiaN = ua.indexOf('NokiaN') > -1;
    //WM
    if (system.win == 'CE') {
        system.winMobile = system.win;
    } else if (system.win == 'Ph') {
        if (/Windows Phone OS (\d+.\d+)/.test(ua)) {
            system.win = "Phone";
            system.winMobile = parseFloat(RegExp['$1']);
        }
    }

    //  检测iOS版本
    if (system.mac && ua.indexOf("Mobile") > -1) {
        if (/CPU (?:iPhone)?OS (\d+_\d+)/.test(ua)) {
            system.ios = parseFloat(RegExp.$1.replace("_".
                "."));
        } else {
            system.ios = 2;
        }
    }
    //  检测安卓
    if (/Android (\d+\.\d+)/.test(ua)) {
        system.android = parseFloat(RegExp.$1);

    }

    //游戏系统
    system.wii = ua.indexOf('Wii') > -1;
    system.ps = /playstation/i.test(ua);
    //返回这些对象
    return {
        engine: engine,
        browser: browser,
        system: system
    };
}();

第十章 DOM

文档对象模型

10.1节点层次

再HTML页面中,文档元素(文档最外层元素)始终都是<Html>

10.1.1Node类型

nodeName nodeValue

节点关系 NodeList parentNode childNode nextSibling

操作节点appendChild() 在末尾添加节点,并返回新增的节点;insertBefore() 在某个特定位置上添加节点;replaceChild()替换节点;cloneNode() 复制节点,可执行深复制

10.1.2Document类型

document.title 文档标题;document.URL URL;document.domain 域名;

查找元素:getElementById()getELementsByTagName()getElementsByName()

文档写入:write()writeln()open()close()

10.1.3Element类型

html元素特性:id,title,lang,dir,class,name

//可获取也可设置
var div = document.getElementById('div');
div.id = 'div2';
dic.className = 'divv';

操作特性:getAttribute(),setAttribute(),removeAttribute(),都没啥用

创建元素

//create
if(client.browser.ie && client.browser.ie <= 7) {
    //  创建一个带name特性的iframe
    var iframe = document.createElement("<iframe name=\"myframe\"></iframe>");

    //创建input元素
    var input = document.createElement("<input type=\"checkbox\">");
    //button
    var button = document.createElement("<button type=\"reset\"></button>");
    //radio
    var radio1 = document.createElement("<input type=\"radio\" name=\"choice\"" + "value=\"1\">");
    var radio2 = document.createElement("<input type=\"radio\" name=\"choice\"" + "value=\"2\">");
}

元素的子节点:空白符什么的;

10.1.4Text类型

创建文本节点

//text
var element = document.createElement('div');
element.className = 'message';

var textNode = document.createTextNode('Hello world');
element.appendChild(textNode);
document.body.appendChild(element);

规范化文本节点

normalize() 将相邻文本节点合并

splieText()分割文本节点

10.1.5Comment类型

注释的内容

10.2DOM操作技术

注意将变量缓存起来,减少DOM操作

10.2.1动态脚本

//动态脚本  外链
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'demo.js';
document.body.appendChild(script);
//封装
function loadScript(url) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;
    document.body.appendChild(script);
}
//行内
function loadScriptString(code) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    try {
        script.appendChild(document.createTextNode(code));
    } catch(e) {
        script.text = code;
    }
    document.body.appendChild(script);
}

10.2.2动态样式

//css
function loadStyles(url) {
    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.type = 'text/css';
    link.href = url;
    var head = document.getElementsByTagName('head')[0];
    head.appendChild(link);
}

function loadStyleString(css) {
    var style = document.createElement('style');
    style.type = 'text/css';
    try {
        style.appendChild(document.createTextNode(css));
    } catch(e) {
        style.styleSheet.cssText = css;
    }

    var head = document.getElementsByTagName('head')[0];
    head.appendChild(style);
}

10.2.3操作表格

//更清晰的创建表格
var table = document.createElement('table');
table.border = 1;
table.width='100%';

var tbody=document.createElement('tbody');
table.appendChild(tbody);

tbody.insertRow(0);
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild(document.createTextNode('cell 1,1'));
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild(document.createTextNode('cell 2,1'));

tbody.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode('cell 1,2'));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode('cell 2,2'));

document.body.appendChild(table);

不定宽元素居中以及元素垂直居中

不定宽元素居中以及元素垂直居中

平常大家对元素进行居中,一般是这个样子的:

    width: xx;
    margin: 0 auto;

设置一个宽度,并使用外边距进行居中。好不好用大家都知道,但宽度不定就不好弄了。

举个例子~

blob.png

今天公司做活动,在首页轮播图上多加了几页,刷新一看,嗯?这底部按钮怎么不居中?,打开控制台一看,嚯

blob.png

直接绝对定位加left做的,这,不好,所以,改。

改改改

直接上最后结果

.banner .num {
    zoom: 1;
    z-index: 30;
    display: table;
    position: relative;
    margin: 0 auto;
    margin-top: -20px;
}

最关键的就是使用display: table;这个属性的说法是什么什么什么。。。

此元素会作为块级表格来显示(类似

),表格前后带有换行符。

嘛,反正最后表现就是有了一个实际的宽度,再配合外边距居中,完美!

然后到360浏览器兼容模式一看,没有了。。。?

虽然知道display: table;不支持IE7一下,单360兼容模式不该是IE8么?我记错了?

blob.png

blob.png

我记错了~

坑爹啊!

没办法,HACK走一波!

.banner .num {
    zoom: 1;
    z-index: 30;
    display: table;
    position: relative;
    margin: 0 auto;
    margin-top: -20px;
    #position: absolute;
    #bottom: 10px;
    #left: 43%;
}

没有了!

对了,还有资料:

html 中 div不定宽度如何水平居中的解决方案  http://blog.csdn.net/phker/article/details/42425305

can i use http://caniuse.com/#search=display%3A%20table

聚奢网 http://www.jushewang.com/

这下真没有了!

2016年5月30补充

<meta http-equiv="x-ua-compatible" content="IE=edge" >

这行代码能使360兼容模式用最新的IE内核渲染~这样的话就少了很多烦恼了,像上面的和box-sizing~~~

2016年8月29日补充

垂直居中

    <main>
        <h1>h1h1h1h1h1</h1>
        <p>ppppppppppppppp</p>
    </main>
    main {
        transition: .5s;
        text-align: center;
        background-color: #333;
        color: #fff;
        width: 20em;
        height: 8em;
        box-sizing: border-box;
        padding: 1em 2em;
    }

绝对定位

        position: absolute;
        left: 50%;
        top: 50%;
        margin-left: -10em;
        margin-top: -4em;

绝对定位·改

        position: absolute;
        left: calc(50% - 10em);
        top: calc(50% - 2em);

绝对定位·改2

        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);

视口单位

        margin: 50vh auto 0;
        transform: translateY(-50%);

flex+align-self

    main {
        align-self: center;
    } 
    body {
        display: flex;
        min-height: 100vh;
        margin: 0;
    }

flex

    main {
        margin: auto;
    }

    body {
        display: flex;
        min-height: 100vh;
    }

内容居中

    <main>
        mmmmmmmmmmmmmmmmmmmm
    </main>
    main {
        display: flex;
        align-items: center;
        justify-content: center;

    }

2016年9月2日补充

writing-mode+margin

    body{
        writing-mode: tb-rl;
        -webkit-writing-mode: vertical-rl;      
        writing-mode: vertical-rl;
    }
    main{
      margin-top:auto;
      margin-bottom:auto;
    }

writing+text-align

    body{
        writing-mode: tb-rl;
        -webkit-writing-mode: vertical-rl;      
        writing-mode: vertical-rl;
        text-align:center;
    }

2016年10月18日补充

display:table

demo

            .box {
                display: table;
                background-color: #000;
                height: 600px;
                width: 1000px;
            }       
            .small {
                background-color: #fff;
                display: table-cell;
                vertical-align: middle;
            }

《javascript高级程序设计》笔记5(第12章 DOM2和DOM3,第13章 事件)

http://www.vastskycc.com/?id=18
#12 DOM2和DOM3

12.1DOM变化

12.1.1 针对XML命名空间的变化

12.1.2 其他方面的变化

12.2样式

12.2.1 访问元素的样式

CSS属性 javascript属性
backgroud-image style.backgroundImage
color style.color
display style.display
font-family style.fontFamily

DOM样式属性和方法

//cssText
myDiv.style.cssText = "width: 25px;height: 100px;";
alert(myDiv.style.cssText);
//length and item
for(var i = 0, len = myDiv.style.length; i < len; i++) {
    alert(myDiv.style[i]); //myDiv.style.item(i)
}
//getPropertyValue
var prop, value, i, len;
for(var i = 0, len = myDiv.style.length; i < len; i++) {
    alert(myDiv.style[i]);
    value = myDiv.style.getPropertyValue(prop);
    alert(prop + ':' + value);
}
//more message
var prop, value, i, len;
for(var i = 0, len = myDiv.style.length; i < len; i++) {
    alert(myDiv.style[i]);
    value = myDiv.style.getPropertyCSSValue(prop);
    alert(prop + ':' + value.cssText + '(' + value.cssValueType + ')');
}
//removeProperty
myDiv.style.removeProperty('border');

计算的样式

//获取受层叠影响的样式信息
myDiv = document.getElementById('myDiv');
var myDivStyle = document.defaultView.getComputedStyle(myDiv, null); //第二个参数可以室伪元素,如“:after”;ie9+
alert(myDivStyle.width);
var myDivStyle = document.defaultView.currentStyle(myDiv); //ie only
alert(myDivStyle.width);

12.2.2 操作样式表

12.2.3 元素大小

偏移量

0821

//offset
function getElementLeft(element) {
    var actualLeft = element.offsetLeft;
    var current = element.offsetParent;
    while(current !== null) {
        actualLeft += current.offsetLeft;
        current = current.offsetParent;
    }
    return actualLeft;
}

function getElementTop(element) {
    var actualTop = element.offsetTop;
    var current = element.offsetParent;
    while(current !== null) {
        actualTop += current.offsetTop;
        current = current.offsetParent;
    }
    return actualTop;
}
//这些属性为只读,每次访问都需要计算,注意性能

客户区大小

0821-2

//client

function getViewport() {
    if(document.compatMode == 'BackCompat') {
        return { //ie7-
            width: document.body.clientWidth,
            height: document.body.clientHeight
        };
    } else {
        return {
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight
        };

    }
}

滚动大小

0821-3

//文档总高度
var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);
//重置滚动
function scrollToTop(element) {
    if(element.scrollTop != 0) {
        element.scrollTop = 0;
    }
}

确定元素大小

//确定元素大小
function getBoundingClientRect(element) {
    var scrollTop = document.documentElement.scrollTop;
    var scrollLeft = document.documentElement.scrollLeft;
    if(element.getBoundingClientRect) {
        if(typeof(arguments.callee.offset) != "number") {
            var scrollTop = document.documentElement.scrollTop;
            var temp = document.createElement('div');
            temp.style.cssText = 'position: absolute;left: 0;top: 0;';
            document.body.appendChild(temp);
            arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
            document.body.removeChild(temp);
            temp = null;
        };

        var rect = element.getBoundingClientRect();
        var offset = arguments.callee.offset;
        return {
            left: rect.left + offset,
            rigfht: rect.right + offset,
            top: rect.top + offset,
            bottom: rect.bottom + offset
        };
    } else {
        var actualLeft = getElementLeft(element);
        var actualTop = getElementTop(element);
        return {
            left: actualLeft - scrollLeft,
            right: actualLeft + element.offsetWidth - scrollLeft,
            top: actualTop - scrollTop,
            bottom: actualTop + element.offsetHeight - scrollTop
        }
    }
}

12.3 遍历

对DOM树的遍历为深度优先

12.3.1 NodeIterator

<div id="div1">
    <p><strong>hellow</strong> world
    </p>
    <ul>
        <li>l1</li>
        <li>l2</li>
        <li>l3</li>
    </ul>
</div>
var div = document.getElementById('div1');
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, null, false); //ie9+
var node = iterator.nextNode();
while(node !== null) {
    console.log(node.tagName); //输出标签名
    node = iterator.nextNode();
}
//只返回Li
var div = document.getElementById('div1');
var filter = function(node) {
    return node.tagName.toLowerCase() == 'li' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter, false);
var node = iterator.nextNode();
while(node !== null) {
    console.log(node.tagName); //输出标签名
    node = iterator.nextNode();
}

12.3.2 TreeWalker

var div = document.getElementById('div1');
var filter = function(node) {
    return node.tagName.toLowerCase() == 'li' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
};
var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, filter, false);
var node = walker.nextNode();
while(node !== null) {
    console.log(node.tagName); //输出标签名
    node = walker.nextNode();
}
var div = document.getElementById('div1');
var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, nulll, false); //ie9+
walker.firstChild(); //转到p
walker.nextSibling(); //转到ul
var node = walker.firstChild(); //转到第一个li
while(node !== null) {
    console.log(node.tagName); //输出标签名
    node = walker.nextSibling()
}
//起点
var node = walker.nextNode();
alert(node === walker.currentNode); //true
walker.currentNode = document.body;

12.4 范围

12.4.1 DOM中的范围

用DOM范围实现简单选择

<p id="p1"><b>hello</b> world</p>
var range1 = document.createRange(); //ie9+
var range2 = document.createRange();
var p1 = document.getElementById('p1');
range1.selectNode(p1);
range2.selectNodeContents(p1);

0821-4

用DOM范围实现复杂选择

//模仿selectNode()selectNodeContents()
var range1 = document.createRange(); //ie9+
var range2 = document.createRange();
var p1 = document.getElementById('p1');
var p1Index = -1;
var i, len;
for(i = 0, len = p1.parentNode.childNodes.length; i < len; i++) {
    if(p1.parentNode.childNodes[i] == p1) {
        p1Index = i;
        break;
    }
}
range1.setStart(p1.parentNode, p1Index + 1); //ie not sup
range1.setEnd(p1.parentNode, p1Index + 1);
range2.setStart(p1, 0);
range2.setEnd(p1, p1.childNodes.length);
var p1 = document.getElementById('p1'),
    helloNode = p1.firstChild,
    worldNode = p1.lastChild,
    range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);

0821-5

操作DOM范围中的内容

0822-1

以前面的例子而言,在he之后自动补了个</b>和`以闭合区间

//delete
var p1 = document.getElementById('p1'),
    helloNode = p1.firstChild,
    worldNode = p1.lastChild,
    range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
range.deleteContents();
<--结果-->
<p id="p1"><b>he</b>rld</p>
//extractContents
var p1 = document.getElementById('p1'),
    helloNode = p1.firstChild,
    worldNode = p1.lastChild,
    range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var fragment = range.extractContents();//
p1.parentNode.appendChild(fragment);
//cloneContents
var p1 = document.getElementById('p1'),
    helloNode = p1.firstChild,
    worldNode = p1.lastChild,
    range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var fragment = range.cloneContents();
p1.parentNode.appendChild(fragment);

插入DOM范围中的内容

<span style="color: red;">Inserted text</span>
var p1 = document.getElementById('p1'),
    helloNode = p1.firstChild.firstChild,
    worldNode = p1.lastChild,
    range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var span = document.createElement('span');
span.style.color = 'red';
span.appendChild(document.createTextNode('Inserted text'));
range.insertNode(span);
<--结果-->
<p id="p1"><b>he<span style="color: red;">Inserted text</span>llo</b> world</p>
var p1 = document.getElementById('p1'),
    helloNode = p1.firstChild.firstChild,
    worldNode = p1.lastChild,
    range = document.createRange();
range.selectNode(helloNode);
var span = document.createElement('span');
span.style.backgroundColor = 'yellow';
range.surroundContents(span);
<--结果-->
<p id="p1"><b><span style="background-color: yellow;">hello</span></b> world</p>

折叠DOM范围

//折叠
range.collapse(true);//折叠到起点
range.collapse(false);//折叠到终点
console.log(range.collapsed);//输出true

0822-3

比较DOM范围

range.compareBoundaryPoints()

复制DOM范围

var newRange = range.cloneRange();

清理DOM范围

range.detach(); //从文档中分离
range = null; //解除引用

12.4.2 IE8及更早版本中的范围

//ie8+
//<p id="p1"><b>hello</b> world</p>

//select hello
var range = document.body.createTextRange();
var found = range.findText('hello');
alert(found); //true
alert(range.text); //hello

var found = range.findText('hello');
var foundAgain = range.findText('hello', 1); //正值往前找,负值往后找

//selectNode
var range = document.body.createTextRange();
var p1 = document.getElementById('p1');
range.moveToElementText(p1);

//html
alert(range.htmlText);
//父节点
var ancestor = range.parentElement();

//ie复杂
range.moveStart('word', 2); //起点移动2个字符
range.moveEnd('character', 1); //起点移动1个字符
range.move('chrarcter', 5); //移动5个字符,范围的起点和终点就一样了,必须创建新选区

var range = document.body.createTextRange();
var p1 = document.getElementById('p1');
range.findText('hello');
range.pasteHTML('<em>wow</em>');

第十三章 事件

JS与HTML之间的交互是通过事件交互的

13.1 事件流

13.1.1事件冒泡

事件开始时由最具体的元素接收,然后逐级向上传播

例:click div

0825

13.1.2 事件捕获

例:click div

0825-1

优先使用冒泡,由特殊需要时再使用事件捕获

13.1.3 DOM事件流

0825-2

1、2、3事件捕获阶段;4处于目标阶段;5、6、7冒泡阶段(4在事件处理中被看成冒泡阶段的一部分)

13.2 事件处理程序

响应某个事件的函数叫做事件处理程序

13.2.1 HTML事件处理程序

<input type="button" id="" value="click" onclick="alert()"/>

和号()、双引号()、小于号()或大于号()注意注意转义

<input type="button" id="" value="click" onclick="showMsg()"/>
<script type="text/javascript">
    function showMsg () {
        alert();
    }
</script>
//event
<input type="button" id="" value="click" onclick="alert(event.type)"/>
//this
<input type="button" id="" value="clicks" onclick="this.value"/>
<input type="button" id="" value="clicks" onclick="value"/>
<form action="">
    <input type="text" id="" value="aa" name="a"/>
    <input type="button" id="" value="bb" name="b" onclick="alert(a.value)"/>
</form>

在HTML中指定事件处理程序的缺点:

1、存在时差问题。不具有执行条件却被执行;

2、浏览器之间的执行差异;

3、HTML与JS代码紧密耦合。

13.2.2 DOM0级事件处理程序

//DOM0
//冒泡阶段被处理
var btn = document.getElementById('myBtn');
btn.onclick = function() {
    alert(this.id); //myBtn
};
//删除
btn.onclick = null;

13.2.3 DOM2级事件处理程序

addEventListener()removeEventListener()

//DOM2
var btn = document.getElementById('myBtn');
btn.addEventListener('click', function() { //ie9+
    alert(this.id);
}, false);

可设置一布尔值为第三个参数,true为捕获阶段处理、false为冒泡阶段处理,默认为false

//可绑定多个事件
var btn = document.getElementById('myBtn');
btn.addEventListener('click', function() {
    alert(this.id);
}, false);
btn.addEventListener('click', function() {
    alert('hello');
}, false);
var btn = document.getElementById('myBtn');
btn.addEventListener('click', function() {
    alert(this.id);
}, false);
btn.removeEventListener('click', function() { //没有用,需要函数名
    alert(this.id);
}, false);
var btn = document.getElementById('myBtn');
var handler = function() {
    alert(this.id);
};
btn.addEventListener('click', handler, false);
btn.removeEventListener('click', handler, false); //有用了

13.2.4 IE事件处理程序

//ie5-10only
var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', function() {
    alert();
});

作用域与DOM0级方法有区别

var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', function() {
    alert(this === window); //true,DOM0级方法中为局部作用域
});
//添加多个事件处理程序
var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', function() {
    alert(1);
});
btn.attachEvent('onclick', function() {
    alert(2);
});
//移除事件处理程序
var btn = document.getElementById('myBtn');
var handler = function() {
    alert(this.id);
};
btn.attachEvent('click', handler);
btn.detachEvent('click', handler);

13.2.5 跨浏览器的事件处理程序

//跨浏览器
var EventUtil = {
    addHandler: function(element, type, handler) {
        if(element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if(element.attachEvent) {
            element.attachEvent('on' + type, handler);
        } else {
            element['on' + type] = handler;
        }
    },
    removeHandler: function(element, type, handler) {
        if(element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if(element.detachEvent) {
            element.detachEvent('on' + type, handler);
        } else {
            element['on' + type] = null;
        }
    }
};
//例子
var btn = document.getElementById('myBtn');
var handler = function() {
    alert(this.id);
};
EventUtil.addHandler(btn, 'click', handler);
EventUtil.removeHandler(btn, 'click', handler);

这里还有作用域不一致的问题,要注意;另外DOM0级每个事件只支持一个处理程序。

13.3 事件对象

event 触发事件时产生的事件对象,包含着所有与事件有关的信息

13.3.1 DOM中的事件对象

属性/方法 类型 读/写 说明
bubbles Boolean 只读 表明事件是否冒泡
cancelabel Boolean 只读 表明是否可以取消事件的默认行为
currentTarget Element 只读 其事件处理程序当前正在处理事件的那个元素
defaultPrevented Boolean 只读 为true表示已经调用了preventDefault()(DOM3)
detail Integer 只读 与事件相关的细节信息
eventPhase Integer 只读 调用事件处理程序的阶段:1表示捕获阶段,2表示‘处于目标’,3表示冒泡阶段
preventDefault() Function 只读 取消事件的默认行为。如果cancelabel是true,则可以使用这个方法
stopImmediatePropagation() Function 只读 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用(DOM3)
stopPropagation() Function 只读 取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法
target Element 只读 事件的目标
trusted Boolean 只读 为true表示事件是浏览器生成的。为false表示是由开发人员通过js创建的(DOM3)
type String 只读 被触发的事件的类型
view AbstractView 只读 与事件关联的抽象视图。等同于发生事件的window对象
var btn = document.getElementById('myBtn');
btn.onclick = function(event) {
    alert(event.currentTarget === this); //true
    alert(event.target === this); //true
};

document.body.onclick = function(event) {
    alert(event.currentTarget === document.body); //true
    alert(this = document.body); //true
    alert(event.target === document.getElementById('myBtn')); //true
}
//一个函数处理多个事件
var btn = document.getElementById('myBtn');
var handler = function(event) {
    switch(event.type) {
        case 'click':
            alert('c');
            break;
        case 'mouseover':
            event.target.style.backgroundColor = 'red';
            break;
        case 'mouseout':
            event.target.style.backgroundColor = '';
            break;
    }
};

btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;
var link = document.getElementById('myLink');
link.onclick = function(event) {
    event.preventDefault();//阻止默认行为,需要cancelabel是true
    event.stopPropagation();//阻止冒泡
    alert(event.eventPhase);//确定事件处理程序的阶段
};

13.3.2 IE中的事件对象

下一节见

13.3.3 跨浏览器的事件对象

var EventUtil = {
    //省略之前的
    getEvent: function(event) {
        return event ? event : window.event;
    },
    getTarget: function(event) {
        return event.target || event.srcElement;
    },
    preventDefault: function(event) {
        if(event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    removeHandler: function(element, type, handler) {
        if(element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if(element.detachEvent) {
            element.detachEvent('on' + type, handler);
        } else {
            element['on' + type] = null;
        }
    },
    stopPropagation: function(event) {
        if(event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelable = true;
        }
    }
};
//例子
btn.onclick = function(event) {
    event = EventUtil.getEvent(event); //传event
    var target = EventUtil.getTarget(event); //传target    
    EventUtil.stopPropagation(event); //阻止冒泡
};
var link = document.getElementById('myLink');
link.onclick = function(event) {
    event = EventUtil.getEvent(event);
    EventUtil.preventDefault(event); //阻止默认行为
};

13.4 事件类型

13.4.1 UI事件

load

//load,完全加载后在window上触发
EventUtil.addHandler(window, 'load', function(event) {
    alert();
});
//<body onload='alert()'>
//图像上也可以触发
//<img src='smile.gif' onload='alert()'>
EventUtil.addHandler(image, 'load', function(event) {
    event = EventUtil.getEvent(event);
    alert(EventUtil.getTarget(evnet).src);
});
//图片加载完后给出提示
EventUtil.addHandler(window, 'load', function() {
    var image = document.createElement('img');
    EventUtil.addHandler(image, 'load', function(event) {
        event = EventUtil.getEvent(event);
        alert();
    });
    document.body.appendChild(image);
    image.src = 'smile.gif';
});
//dom0
EventUtil.addHandler(window, 'load', function() {
    var image = new Image();
    EventUtil.addHandler(image, 'load', function(event) {
        alert();
    });
    image.src = 'smile.gif';
});
//为scipt元素指定事件处理程序
EventUtil.addHandler(window, 'load', function() {
    var script = document.createElement('scirpt');
    EventUtil.addHandler(script, 'load', function(event) {
        alert();
    });
    script.src = 'example.js';
    document.body.appendChild(script);
});

unload

//unload
EventUtil.addHandler(window, 'unload', function(event) {
    alert();
});
//<body onunload='alert()'>

resize

//resize
EventUtil.addHandler(window, 'resize', function(event) {
    alert();
}); //可能被频繁执行

scroll

//scroll
EventUtil.addHandler(window, 'scroll', function(event) {
    if(document.compatMode == 'CSS1Compat') {
        alert(document.documentElement.scrollTop);
    } else {
        alert(document.body.scrollTop);
    }
});

13.4.2 焦点事件

blur失去焦点时触发,不冒泡

focus获得焦点时触发,不冒泡

focusout失去焦点时触发,冒泡

focusin获得焦点时触发,不冒泡

13.4.3 鼠标与滚轮事件

鼠标事件

click单击或按下回车键触发

dbclick双击触发

mousedown按下了任意鼠标按钮时触发

mouseenter在鼠标光标从元素外部首次移动到元素范围之内时触发

mouseleave在位于元素上方的鼠标光标移动到元素范围之外时触发

mousemove在元素内部移动时重复地触发

mouseout在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发

mouseover从元素外部移到另一个元素之内

mouseup释放鼠标按钮时触发

双击:1、mousedown;2、mouseup;3、click;4、mousedown;5、mouseup;6、click;7、dbcklick。

滚轮事件

mousewheel

客户区坐标位置

在视口中的水平和垂直坐标

//客户区坐标位置
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, 'click', function(event) {
    event = EventUtil.getEvent(event);
    alert('Client coordinates:' + event.clientX + ',' + event.clientY);
});

页面坐标位置

在页面中的水平和垂直坐标,在无滚动的情况下,(pageX,pageY)=(clientX,clientY)

//鼠标在页面中的坐标
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, 'click', function(event) {
    event = EventUtil.getEvent(event);
    alert('page coordinates:' + event.pageX + ',' + event.pageY);
});
//ie8-
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, 'click', function(event) {
    event = EventUtil.getEvent(event);
    var pageX = event.pageX,
        pageY = event.pageY;
    if(pageX === undefined) {
        pageX = event.clientX + (document.body.scrollLeft || ddocument.documentElement.scrollLeft)
    };
    if(pageY === undefined) {
        pageY = event.clientY + (document.body.scrollTop || ddocument.documentElement.scrollTop)
    }

    alert('page coordinates:' + event.pageX + ',' + event.pageY);
});

屏幕坐标位置

在整个屏幕中的水平和垂直坐标

//屏幕坐标位置
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, 'click', function(event) {
    event = EventUtil.getEvent(event);
    alert('Screen coordinates:' + event.screenX + ',' + event.screenY);
});

修改键

//修改键
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, 'click', function(event) {
    event = EventUtil.getEvent(event);
    var keys = new Array();
    if(event.shiftKey) {
        keys.push('shift');
    }
    if(event.ctrlKey) {
        keys.push('ctrl');
    }
    if(event.altKey) {
        keys.push('alt');
    }
    if(event.metaKey) {
        keys.push('meta');
    }
    alert('keys:' + keys.join(','));
});

相关元素

鼠标移入移出相关

var EventUtil = {
  //省略
    getRelatedTarget: function(event) {
        if(event.relatedTarget) {
            return event.relatedTarget;
        } else if(event.toElement) {
            return event.toElement;
        } else if(event.fromElement) {
            return event.fromElement;
        } else {
            return null;
        }
    }
};
//例子
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, 'mouseout', function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    var relatedTarget = EventUtil.getRelatedTarget(event);
    alert('mouse out of' + target.tagName + 'to' + relatedTarget.tagName);
});

鼠标按钮

var EventUtil = {
//...
    getButton: function(event) { //add
        if(document.implementation.hasFeature('MouseEvents', '2.0')) {
            return event.button;
        } else {
            switch(event.button) { //兼容ie8-
                case 0:
                case 1:
                case 2:
                case 3:
                case 7:
                    return 0; //主鼠标按钮,左键
                case 2:
                case 6:
                    return 2; //中键,滚轮
                case 4:
                    return 1; //次鼠标按钮,右键
            }
        }
    }
};
//例子
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, 'click', function(event) {
    event = EventUtil.getEvent(event);
    alert(EventUtil.getButton(event));
});

更多的事件信息

event.detail

鼠标滚轮事件

//mousewheeel,除了ff
EventUtil.addHandler(document, 'mousewheel', function(event) {
    event = EventUtil.getEvent(event);
    alert(EventUtil.wheelDelta); //滚轮向前为120的倍数,向后为-120的备份
});
//兼容opera 9.5-
EventUtil.addHandler(document, 'mousewheel', function(event) {
    event = EventUtil.getEvent(event);
    //第九章的代理检测
    var delta = (client.engine.opera && client.engine.opera < 9.5 ?
        -event.wheelDelta : event.wheelDelta
    );
    alert(delta);
});
//ff
EventUtil.addHandler(window, 'DOMMouseScroll', function(event) {
    event = EventUtil.getEvent(event);
    alert(EventUtil.detail); //滚轮向前为-3的倍数,向后为3的备份
});
var EventUtil = {
    //add mousewheel
    getWheelDelta: function(event) {
        if(event.wheelDelta) {
            return(client.engine.opera && client.engine.opera < 9.5 ?
                -event.wheelDelta : event.wheelDelta);
        } else {
            return -event.detail * 40;
        }
    },
};
//demo
(function() {
    function handleMouseWheel(event) {
        event = EventUtil.getEvent(event);
        var delta = EventUtil.getWheelDelta(event);
        alert(delta);
    }
    EventUtil.addHandler(document, 'mousewheel', handleMouseWheel);
    EventUtil.addHandler(document, 'DOMMouseScroll', handleMouseWheel);
})();

触摸设备

不支持dbclick事件

轻击可单击元素会触发mousemove事件

mousemove事件会触发mouseovermouseout事件

两个手指放在屏幕上页面随手指而滚动会触发mousewheelscroll事件

无障碍性问题

少用鼠标移到多用点击,不要双击的

13.4.4 键盘与文本事件

keydownkeypresskeyup

键码

var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox, 'keyup', function(event) {
    event = EventUtil.getEvent(event);
    alert(event.keyCode);
});

字符编码

var EventUtil = {
    //add charcode
    getCharCode: function(event) {
        if(typeof(event.charCode) == "number") {
            return event.charCode;
        } else {
            return event.keyCode; //ie8-
        }
    },
};
//demo
var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox, 'keypress', function(event) {
    event = EventUtil.getEvent(event);
    alert(event.getCharCode(event));
});
//key
//兼容问题,不推荐使用
var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox, 'keypress', function(event) {
    event = EventUtil.getEvent(event);
    var identifier = event.key || event.keyIdentifier;
    if(identifier) {
        alert(identifier);
    }
});
//location
var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox, 'keypress', function(event) {
    event = EventUtil.getEvent(event);
    var loc = event.Location || event.keyLocation;
    if(loc) {
        alert(loc);
    }
});

var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox, 'keypress', function(event) {
    event = EventUtil.getEvent(event);
    if(event.getModifierState) {
        alert(event.getModifierState('Shift'));
    }
});

//textinput
var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox, 'textInput', function(event) {
    event = EventUtil.getEvent(event);
    alert(event.data);
});

event.inputMethod表示把文本输入到文本框中的方式

设备中的键盘事件

遥控器判断键码,屏幕键盘触发键盘事件

13.4.5 复合事件

composition event

13.4.6 变动事件

删除、插入触发的一堆事件

13.4.7 HTML5事件

contextmenu事件

右键菜单

<!doctype html>
<html lang="en">

    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>

    <body>
        <div id="myDiv">demo</div>
        <ul id="myMenu" style="position: absolute;visibility: hidden;background-color: #AAB8C2;">
            <li>11</li>
            <li>22</li>
            <li>33</li>
        </ul>
    </body>
</html>
EventUtil.addHandler(window, 'load', function(event) {
    var div = document.getElementById('myDiv');
    EventUtil.addHandler(div, 'contextmenu', function(event) {
        event = EventUtil.getEvent(event);
        EventUtil.preventDefault(event);

        var menu = document.getElementById('myMenu');
        menu.style.left = event.clientX + 'px';
        menu.style.top = event.clientY + 'px';
        menu.style.visibility = 'visible';

    });
    EventUtil.addHandler(document, 'click', function(event) {
        document.getElementById('myMenu').style.visibility = 'hidden';
    });
});

beforeunload事件

在浏览器卸载页面之前触发

//beforeunload
EventUtil.addHandler(window, 'beforeunload', function(event) {
    event = EventUtil.getEvent(event);
    var msg = 'hello';
    event.returnValue = msg;
    return msg;
});

DOMContentLoaded事件

//DOMContentLoaded
EventUtil.addHandler(document, 'DOMContentLoaded', function(event) {
    alert('content loaded');
});

//not support  DOMContentLoaded
setTimeout(function() {
    //add event
}, 0);

readystatechange事件

//readystatechange
EventUtil.addHandler(window, 'load', function(event) {
    var script = document.createElement('script');
    EventUtil.addHandler(script, 'readystatechange', function(event) {
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        if(target.readyState == 'loaded' || target.readyState == 'complete') {
            EventUtil.removeHandler(target, 'readystatechange', arguments.callee);
            alert('script loaded');
        }
    });
    script.src = 'example.js';
    document.body.appendChild(script);
});

pageshow和pagehide事件

bfcache缓存

haschage

//haschange
EventUtil.addHandler(window, 'haschange', function(event) {
    alert('old url:' + event.oldURL + '\nNew URL' + event.newURL);
    alert('current hash:' + location.hash);
});

var isSupported = ('onhashchange' in window) && (document.documentMode === undefined || document.documentMode > 7);

13.4.8 设备事件

orientationchange事件

屏幕旋转,0为竖直,90为向左的横屏模式,-90为向右,180为倒转

//orientationchange
//ios only
EventUtil.addHandler(window, 'load', function(event) {
    var div = document.getElementById(',yDiv');
    div.innerHTML = 'Current orientation is' + window.orientation;
    EventUtil.addHandler(window, 'orientationchange', function(event) {
        div.innerHTML = 'Current orientation is' + window.orientation;
    });
});

MozOrientation事件

加速计检测到设备方向改变时触发

deviceorientation事件

表示设备在空间朝像哪,x、y、z轴

//deviceorientation
EventUtil.addHandler(window, 'deviceorientation', function(event) {
    var output = document.getElementById('output');
    output.innerHTML = 'Alpha:' + event.alpha + ', Beta:' + event.beta + ', Gamma:' + event.gamma + '<br>';
});
//设备方向改变而旋转元素
EventUtil.addHandler(window, 'deviceorientation', function(event) {
    var arrow = document.getElementById('arrow');
    arrow.style.transform = 'rotate(' + Math.round(event.alpha)) + 'deg)';
});

devicemotion事件

设备什么时候移动

//devicemotion
EventUtil.addHandler(window, 'deviceorientation', function(event) {
    var output = document.getElementById('output');
    if(event.rotationRate !== null) {
        output.innerHTML += 'Alpha=' + event.rotationRate.alpha + ', Beat' + event.rotationRate.beta + ', Gamma' + event.rotationRate.gamma;
    }
});

13.4.9 触摸与手势事件

Touch Events规范

触摸事件

//touch
function handleTouchEvent(event) {
    //只跟踪一次触摸
    if(event.touches.length == 1) {
        var output = document.getElementById('output');
        switch(event.type) {
            case 'touchstart':
                output.innerHTML = 'touch started(' + event.touches[0].clientX + ',' + event.touches[0].clientY + ')';
                break;
            case 'touchend':
                output.innerHTML += '<br>touch ended(' + event.touches[0].clientX + ',' + event.touches[0].clientY + ')';
                break;
            case 'touchmove':
                event.getPreventDefault(); //阻止滚动
                output.innerHTML += '<br>touch moveed(' + event.touches[0].clientX + ',' + event.touches[0].clientY + ')';
                break;
        }
    }
}

EventUtil.addHandler(document, 'touchstart', handleTouchEvent);
EventUtil.addHandler(document, 'touchend', handleTouchEvent);
EventUtil.addHandler(document, 'touchmove', handleTouchEvent);

手势事件

//gesture
function handleGestureEvent(event) {
    //只跟踪一次触摸

    var output = document.getElementById('output');
    switch(event.type) {
        case 'gesturestart':
            output.innerHTML = 'gesture started(rotation=' + event.rotation + ',scale=' + event.scale + ')';
            break;
        case 'gestureend':
            output.innerHTML += '<br>gesture ended(rotation=' + event.rotation + ',scale=' + event.scale + ')';
            break;
        case 'gesturechange':
            event.getPreventDefault(); //阻止滚动
            output.innerHTML += '<br>gesture changed(' + event.rotation + ',scale=' + event.scale + ')';
            break;
    }

}

document.addEventListener('gesturestart', handleGestureEvent, false);
document.addEventListener('gestureend', handleGestureEvent, false);
document.addEventListener('gesturechange', handleGestureEvent, false);

13.5 内存和性能

13.5.1 事件委托

<ul id="myLinks">
    <li id="a1">a</li>
    <li id="a2">aa</li>
    <li id="a3">aaa</li>
</ul>
var list = document.getElementById('myLinks';)
EventUtil.addHandler(list, 'click', function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    switch(target.id) {
        case 'a1':
            document.title = '1';
            break;
        case 'a2':
            document.title = '2';
            break;
        case 'a3':
            document.title = '3';
            break;
    }
});

13.5.2 移除事件处理程序

<div id="myDiv">
    <input type="button" id="myBtn" value="" />
</div>
btn.onclick = function() {
    //do
    btn.onclick = null;
    document.getElementById('myDiv').innerHTML = 'processing..';
};

13.6 模拟事件

13.6.1 DOM中的事件模拟

模拟鼠标事件

var btn = document.getElementById('myBtn');
var event = document.createEvent('MouseEvents'); //创建事件对象
event.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); //初始化事件对象
btn.dispatchEvent(event); //触发事件

模拟键盘事件

var textbox = document.getElementById('myTextbox'),
    event;

//以DOM3级方式创建对象
if(document.implementation.hasFeature("KeyboardEvents", '3.0')) {
    event = document.createEvent('KeyboardEvents');

    //初始化事件对象
    event.initKeyboardEvent('keydown', true, true, document.defaultView, 'a', 0, 'Shift', 0);

}
//触发事件
textbox.dispatchEvent(event);
//模拟了shift+A
//ff only
var textbox = document.getElementById('myTextbox');
var event = document.createEvent('KeyEvents'); //创建事件对象
//初始化事件对象
event.initKeyEvent('keypress', true, true, document.defaultView, false, false, false, false, 65, 65);
//触发事件
textbox.dispatchEvent(event);
//模拟了在输入框输入字母A
//other
var textbox = document.getElementById('myTextbox');
var event = document.createEvent('Events'); //创建事件对象
//初始化事件对象
event.initEvent(type, bubbles, cancelable);
event.view = document.defaultView;
event.altKey = false;
event.ctrlKey = false;
event.shiftKey = false;
event.metaKey = false;
event.keyCode = 65;
event.charCode = 65;
//触发事件
textbox.dispatchEvent(event);

模拟其他事件

模拟变动事件和HTML事件

自定义DOM事件

//自定义事件
var div = document.getElementById('myDiv'),
    event;
EventUtil.addHandler(div, 'myevent', function(event) {
    alert('div:' + event.detail);
});
EventUtil.addHandler(document, 'myevent', function(event) {
    alert('document:' + event.detail);
});
if(document.implementation.hasFeature('CustomEvents', '3.0')) {
    event = document.createEvent('CustomEvents');
    event.initCustomEvent('myevent', true, false, 'hello');
    div.dispatchEvent(event);
};
//创建了冒泡事件myevent

13.6.2 IE中的事件模拟

//ie模拟click
var btn = document.getElementById('myBtn');
//创建事件对象
var event = document.createEventObject();
//初始化事件对象
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY = 0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;
//触发事件
btn.fireEvent('onclick', event);
//ie模拟keypress
var textbox = document.getElementById('myTextbox');
var event = document.createEventObject(); //创建事件对象
//初始化事件对象

event.altKey = false;
event.ctrlKey = false;
event.shiftKey = false;
event.keyCode = 65;

//触发事件
textbox.fireEvent('onkeypress', event);

# CSS reset by Eric A. Meyer

《css权威指南》的作者Eric A. Meyer写的重置css.

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

http://meyerweb.com/eric/

凑更完成

前端模块化浅见

blog

前端模块化这个概念太大了,这里只能粗略讲(wa)一(ge)下(keng)

模块化是为了更方便的实现复用,提高编程效率,是手段而不是目的,最终目标是什么,想了想,说是为了解决问题总不错吧,嘿嘿。

前端上的模块化也不算啥新概念,css中就有@import,js倒是晚了点,也有啥cmdamd和ES6module,至于html页面的模块化,额,iframeiframe虽然问题不少,但确实是一种前端模块化方案,此中优劣,我不多说,各位前辈多有论述,现在的主流意见是不推荐使用,那除此之外的方法有啥呢。

js异步添加

举个例子,jquery ajax的load方法加载html:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script src="https://lib.baomitu.com/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
	<div id="header"></div>
	<script>
	$(function(){
				$('#header').load('header.html #headers',function(){
		})
	})
	</script>
</body>
</html>

上面加载的headers页面

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<div id="headers">
        <style type="text/css">
			#headers {
				color: #ca1212;
			}
		</style>
		233333
	</div>
</body>
</html>

加载完毕后:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>Document</title>
		<script src="https://lib.baomitu.com/jquery/3.1.1/jquery.min.js"></script>
	</head>

	<body>
		<div id="header">
			<div id="headers">
              	<style type="text/css">
					#headers {
						color: #ca1212;
					}
				</style>
				233333
			</div>
		</div>
		<script>
			$(function() {
				$('#header').load('header.html #headers', function() {})
			})
		</script>
	</body>
</html>

也不是什么新东西,应该是传统前端页面模块化比较合适的方案,html、css、js都可以通过AJAX加载,缺点也是有的,不过还是比较稳的~

基于框架的组件系统

没错,说的就是当下大热的react组件,以及更方便的vue单文件(感觉都组件到极致),这里借下别人的项目:

缺点有一些,得用上框架(react/vue)、构建系统(webpack)和配套的加载器(loader)还有包管理器(NPM),其实就是从传统前端转变到现代前端的一些学习成本,换个角度,学习新东西能够提升效率、解决问题,显然优点更大一点

2017年2月28日更新

HTML引入

教程

被加载的页面:

<div class="warning">
  <style scoped>
    h3 {
      color: red;
    }
  </style>
  <h3>Warning!</h3>
  <p>This page is under construction</p>
</div>

<div class="outdated">
  <h3>Heads up!</h3>
  <p>This content may be out of date</p>
</div>

导入

<head>
  <link rel="import" href="warnings.html">
</head>
<body>
  ...
  <script>
    var link = document.querySelector('link[rel="import"]');
    var content = link.import;

    // 从 warning.html 的文档中获取 DOM。
    var el = content.querySelector('.warning');

    document.body.appendChild(el.cloneNode(true));
  </script>
</body>

总结

从最传统的iframe,到稍显优雅的AJAX,直至组件的应用,在现今的情况下,都有其使用的场景,我们要做的,就是根据项目实际,选择最合适的方案。

以上!

过气网红还有人记得么

一个行列式布局库和一个基础库

行列式布局这个是从菜鸟教程上拿来的,按自己需要加上了十分之N的列,适合生成方方正正的页面。有用到box-sizing:border-box;所以兼容性为IE8以上,其他浮动清浮动什么的也没啥问题。代码不多,直接粘上来了。

.row * {
    box-sizing: border-box;
}

.row:after {
    content: "";
    clear: both;
    display: block;
}

[class*="col-"] {
    float: left;
    position: relative;
}

.col-01 {
    width: 10%;
}

.col-02 {
    width: 20%;
}

.col-03 {
    width: 30%;
}

.col-04 {
    width: 40%;
}

.col-05 {
    width: 50%;
}

.col-06 {
    width: 60%;
}

.col-07 {
    width: 70%;
}

.col-08 {
    width: 80%;
}

.col-09 {
    width: 90%;
}

.col-010 {
    width: 100%;
}

.col-1 {
    width: 8.33%;
}

.col-2 {
    width: 16.66%;
}

.col-3 {
    width: 25%;
}

.col-4 {
    width: 33.33%;
}

.col-5 {
    width: 41.66%;
}

.col-6 {
    width: 50%;
}

.col-7 {
    width: 58.33%;
}

.col-8 {
    width: 66.66%;
}

.col-9 {
    width: 75%;
}

.col-10 {
    width: 83.33%;
}

.col-11 {
    width: 91.66%;
}

.col-12 {
    width: 100%;
}

然后是公司的公共库,这个嘛,是我领导以前写的,跟张鑫旭张大神写的很像。将常用的属性、赋值提取出来,用在小杂碎上不需要再单独想类名这样的使用场景。

当然了,项目做大了就该用上用SASS或LESS啦。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.