分类目录归档:JavaScript

fetch 用法简介

Fetch API提供了一个获取资源的接口(包括跨域)。任何使用过 XMLHttpRequest 的人都能轻松上手,但新的API提供了更强大和灵活的功能集。

参数

第一个参数是URL地址。

第二个参数(可选)是fetch使用的选项(method、headers等选项)

返回结果

fetch会返回一个promise对象,resolve对应请求的Response。

response属性:

  1. body
  2. bodyUsed

    一个布尔值来标示该Response是否读取过Body

  3. headers

    此Response所关联的Headers 对象.

  4. ok

    一个布尔值来标示该Response成功(状态码200-299) 还是失败.

  5. redirected

    该Response是否来自一个重定向,如果是的话,它的URL列表将会有多个

  6. status

    Response的状态码 (例如, 200 成功).

  7. statusText

    与该Response状态码一致的状态信息 (例如, OK对应200).

  8. type

    Response的类型 (例如, basic, cors).

  9. url

    Response的URL.

response方法

  1. arrayBuffer()

    读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为ArrayBuffer格式的promise对象

  2. blob()

    读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为Blob格式的promise对象

  3. formData()

    读取Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为FormData格式的promise对象

  4. json()

    读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为JSON格式的promise对象

  5. text()

    读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为USVString格式的promise对象

示例:读取一个图片并生成URL

var myImage = document.querySelector('.my-image');
fetch('flowers.jpg').then(function(response) {
  return response.blob();
}).then(function(response) {
  var objectURL = URL.createObjectURL(response);
  myImage.src = objectURL;
});

get请求

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });

post请求

fetch(url, {
  method: 'POST',
  body: JSON.stringify(data),
  headers: new Headers({
    'Content-Type': 'application/json'
  })
})

上传文件&提交表单

var formData = new FormData();
var fileField = document.querySelector("input[type='file']");

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/upload', {
  method: 'PUT',
  body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));

移动端禁用和启用滚动、拖动

弹窗和弹框是网站应用中经常用到的技术,但是在页面弹出一个全屏的弹窗的时候,还是可以使用触摸来滑动弹窗下面的页面。

为了用户有更好的体验,可以使用以下代码,在弹窗的时候禁用滚动,弹窗关闭的时候再次启用滚动。

// left: 37, up: 38, right: 39, down: 40,
// spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36
var keys = {37: 1, 38: 1, 39: 1, 40: 1};

function preventDefault(e) {
    e = e || window.event;
    if (e.preventDefault)
        e.preventDefault();
    e.returnValue = false;
}

function preventDefaultForScrollKeys(e) {
    if (keys[e.keyCode]) {
        preventDefault(e);
        return false;
    }
}

function disableScroll() {
    if (window.addEventListener) // older FF
        window.addEventListener('DOMMouseScroll', preventDefault, false);
    window.onwheel = preventDefault; // modern standard
    window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
    window.ontouchmove  = preventDefault; // mobile
    document.onkeydown  = preventDefaultForScrollKeys;
}

function enableScroll() {
    if (window.removeEventListener)
        window.removeEventListener('DOMMouseScroll', preventDefault, false);
    window.onmousewheel = document.onmousewheel = null;
    window.onwheel = null;
    window.ontouchmove = null;
    document.onkeydown = null;
}

jQuery()函数的4中调用方式

jQuery()函数的4中调用方式

选择元素

$(selector)
$(selector,context)

封装成jQuery对象

$(Element|Document|Window)

创建jQuery对象

$('<img/>')
$('<img/>',{
    src: url
})

传入函数,在文档加载完毕运行

DOMContentLoaded

jQuery(function(){});
$(document).ready(function(){});

使用ES6的Proxy实现简单的双向绑定

ES6的Proxy对象可以用来拦截一个Object对象的属性的修改,这次我们利用它来实现一个简单的双向绑定。
废话不多说,请看代码。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>View to model</title>
</head>
<body>
    <div>
        <!-- 使用'bind-to'的属性来标记这个输入框里的值会对Model里的属性进行修改 -->
        <input bind-to="name" />
        <input bind-to="name" />
    </div>
    <!-- 使用#属性名#来把Model里的值映射到DOM中 -->
    <span>#name#</span>
    <span>#name#</span>
    <button onclick="reset()">reset</button>
    <script type="text/javascript">
        var handler = {
            // 拦截属性的设置,触发试图的更新
            set: function(target, key, value, receiver) {
                target[key] = value;
                updateView(propertyName);
                return Reflect.set(target, key, value);
            },
        };
        // 双向绑定的对象
        var model = new Proxy({}, handler);
        // 双向绑定列表
        var inputs = {},views = {};
        var all = document.all;
        // 遍历所有DOM元素
        for(var i=0,l=all.length;i<l;i++){
            // 找到所有有bind-to的输入框
            if(all[i].getAttribute('bind-to')){
                var dom = all[i];
                var propertyName = dom.getAttribute('bind-to');
                inputs[propertyName] = inputs[propertyName] ? inputs[propertyName] : [];
                views[propertyName] = views[propertyName] ? views[propertyName] : [];
                inputs[propertyName].push(dom);
                dom.addEventListener('change',function function_name(e) {
                    var propertyName = e.target.getAttribute('bind-to');
                    model[propertyName] = e.target.value;
                })
            }
            // 找到所有model驱动的页面元素
            if(all[i].innerHTML && all[i].innerHTML.length>2 &&
                all[i].innerHTML[0] == '#' &&
                all[i].innerHTML[all[i].innerHTML.length-1] == '#'){
                var propertyName = all[i].innerHTML.slice(1,all[i].innerHTML.length-1);
                inputs[propertyName] = inputs[propertyName] ? inputs[propertyName] : [];
                views[propertyName] = views[propertyName] ? views[propertyName] : [];
                views[propertyName].push(all[i]);
            }
        }
        // 更新View
        function updateView(propertyName) {console.log('update ' + propertyName);
            if(views[propertyName]){
                for(var i = 0,l = views[propertyName].length;i < l;i++){
                    views[propertyName][i].innerText = model[propertyName];
                }
            }
            if(inputs[propertyName]){
                for(var i = 0,l = inputs[propertyName].length;i < l;i++){
                    inputs[propertyName][i].value = model[propertyName];
                }
            }
        }
        function reset() {
            for(var name in model){
                model[name] = '';
            }
        }
    </script>
</body>
</html>

让很多Promise一个接一个进行

Promise是我们处理异步操作的时候经常用到的。
有的时候我们会需要顺序执行很多异步操作,如果操作比较少的时候还可以写成a.then(b).then(c)...,可是如果异步操作很多的时候就不能这样写了。
可以利用数组的reduce函数达到同样的效果,这个时候不管有多少异步操作都可以连在一起了。
代码如下:

function waitPromise(time) {
    console.log(time);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('resolved');
        }, time);
    });
}
function log(data) {
    return new Promise((resolve, reject) => {
        console.log(data + '@' + (new Date().getSeconds()));
        resolve();
    });
}
var ps = [];
for (var i = 0; i < 3; i++) {
    let time = (i + 1) * 1000;
    ps.push(() => waitPromise(time));
    ps.push(log);
}
console.log('started' + '@' + (new Date().getSeconds()));
var p = Promise.resolve();
ps.reduce((p, c) => {
    return p.then(c)
}, p).then(() => {
    console.log('all finished');
}).catch(reject => {
    console.log('reject', reject)
});

输出结果:

started@59
1000
resolved@0
2000
resolved@2
3000
resolved@5
all finished

 

jQuery.extend 和 jQuery.fn.extend 的区别

jQuery.extend

jQuery.extend可以扩展jQuery对象本身。
用来在jQuery命名空间上增加新函数,或者合并对象。
1. 增加方法:

$.extend({
  hello: function () {
    console.log(this)//function jQuery(selector, context)
  }
});
$.hello();
  1. 合并对象
var obj1 = {'name' : 'sheng00'};
var obj2 = {'sex' : 'Male'};
$.extend(obj1, obj2);
console.log(obj1)//Object {name: "sheng00", sex: "Male"}

jQuery.fn.extend

用来扩展 jQuery 元素集来提供新的方法(通常用来制作插件)

$.fn.extend({
  turn_red: function () {
    return this.each(function () {
      this.style.color = 'red'
    });
  }
});
$('.elements').turn_red(); // sets color to red

效果和jQuery.fn.turn_red=function(){}是一样的

AngularJS动态应用filter

有的时候我们在动态生成页面内容的时候,需要动态应用filter,这里和大家分享一下方法

假如我们有这样一组数据

[
  {val:"text"},
  {val:"upper text",filter:"uppercase"},
  {val:new Date(),filter:"date",formatter:"yyyy-MM-dd"}
]

数据的值和格式都是动态的
这时候我们需要再写一个filter去动态应用数据的格式

动态应用filter和filter格式的filter

angular.module("app",[])
.filter('use_filter', function ($filter) {
  return function (value, filterName, formatter) {
    return filterName ?
            (formatter ?
                $filter(filterName)(value, formatter) :
                $filter(filterName)(value)) :
            value;
  };
});

使用的时候:

{{item.val| use_filter:item.filter:item.formatter}}

全部代码:

<!DOCTYPE html>
<html ng-app="app">
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
    <script src="http://cdn.staticfile.org/angular.js/1.3.0-beta.13/angular.js"></script>
</head>
<body ng-controller="ctrl">
<div class="container">
    <ul>
        <li ng-repeat="item in data">
            <span>{{item| json}}</span>{{item.val| use_filter:item.filter:item.formatter}}
        </li>
    </ul>
</div>
<script>
angular.module("app",[])
.controller("ctrl",function($scope){
    $scope.data = [
        {val:"text"},
        {val:"upper text",filter:"uppercase"},
        {val:new Date(),filter:"date",formatter:"yyyy-MM-dd"}
    ];
})
.filter('use_filter', function ($filter) {
    return function (value, filterName, formatter) {
        return filterName ?
                (formatter ?
                    $filter(filterName)(value, formatter) :
                    $filter(filterName)(value)) :
                value;
    };
});
</script>
</body>
</html>

直接运行代码:

提示:你可以先修改部分代码再运行。

使用angularjs directive生成api方法供其他地方调用angularjs directive expose api

有些时候我们需要directive生成api,可以在其他地方(controller)调用
image1419911269
下面介绍下简单的步骤:

使用directive的双向绑定绑定controller里的变量

html:
<div expose="exposedApi"></div>
js:

directive('expose',function(){
  return {
    restrict: "A",
    scope: {
      api: "=expose"
    }
  };

这个时候controller离的exposedApi变量和directive里的api是双向绑定的

给directive里的api增加可调用的方法

directive('expose',function(){
  return {
    restrict: "A",
    scope: {
      api: "=expose"
    },
    controller: function($scope){
      $scope.number = 0;
      $scope.api={
        count:function(){
          $scope.number ++;
        }
      };
    },
    template: '<div class="well">' +
              '<p>count: {{number}}</p>' +
              '</div>'
  };

调用directive里的方法

controller("ctrl",function($scope){
  $scope.count = function(){
    $scope.exposedApi.count();
  };
})

Demo:

http://shengoo.github.io/angularjs-practice

完整代码:

<!DOCTYPE html>
<html ng-app="app">
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <link rel="stylesheet" href="../bower_components/bootstrap/dist/css/bootstrap.css" />
  <script src="../bower_components/angular/angular.js"></script>
</head>
<body ng-controller="ctrl">
<div class="container">
<div expose="exposedApi"></div>
<a ng-click="count()" class="btn btn-primary">click</a>
<div expose="exposedApi2"></div>
<a ng-click="count2()" class="btn btn-primary">click</a>
</div>
<script>
angular.module("app",[])
.controller("ctrl",function($scope){
  $scope.count = function(){
    $scope.exposedApi.count();
  };
  $scope.count2 = function(){
    $scope.exposedApi2.count();
  };
})
.directive('expose',function(){
  return {
    restrict: "A",
    scope: {
      api: "=expose"
    },
    controller: function($scope){
      $scope.number = 0;
      $scope.api={
        count:function(){
          $scope.number ++;
        }
      };
    },
    template: '<div class="well">' +
              '<p>count: {{number}}</p>' +
              '</div>'
  };
})
</script>
</body>
</html>