苏苏的博客

简约至极

实用的Javascript代码锦集

实用的Javascript代码锦集

Date.parse("2016-04-4")

Date.parse safari 不能使用单位数字的月份和日期,否则解析为NaN

火狐较低版本(火狐43)也是如此

+new Date('2017-06-08 21:00:00');IE11上得出NaN Safari10 上也得出NaN Chrome 和 Firfox下OK 原因在于 IE 和 Safari 都不识别以-分割的日期 需要写成 +new Date('2017/06/08 21:00:00'); , 这样才能在所有浏览器兼容.

IE10 has not anchorNode focusNode 属性, 取得选中的文字需要读取 text 属性, IE11 一切正常 , 取得选中文字可以直接 toString

IE10,IE11 不支持 html5 的 download 属性, 无法点击下载自定义名称的html和图片等文件. 可以使用 下载的 downloadFile,利用ajax下载,顺便说 ajax 下载也不能使用fetch IE系统统不支持fetch,到EDGE14才支持fetch, https://github.com/github/fetch 可支持IE10+

火狐不支持和chrome的scrollTop表现也不相同

// 兼容处理 chrome 和 firefox
let scrollTop=document.body.scrollTop?document.body.scrollTop:document.documentElement.scrollTop;
if(scrollTop<20){
    this.headerShowStatus=true;
}else if(scrollTop>170){
    this.headerShowStatus=false;
}
if(document.documentElement.scrollHeight-scrollTop- document.documentElement.clientHeight< 400 ){
    this.loadMore();
}

火狐不支持 e.srcElement 需要使用 e.target 代替

IE10,IE11, 不支持location.origin

var origin = location.origin?location.origin:location.protocol+"//"+location.host;

firefox 浏览器下载接受的后端header 不支持单引号

旧版firefox 不支持insertAdjacentElement,直到火狐48才支持

https://gist.github.com/kafene/8e56adaf3c1ca4188938

if ("undefined" === typeof (Element.prototype.insertAdjacentElement)) {
    Object.defineProperty(Element.prototype, "insertAdjacentElement", {
        enumerable: false,
        writable: true,
        configurable: true,
        value: function (position, elem) {
            switch (position.toString().toUpperCase()) {
                case "BEFOREBEGIN":
                    return this.parentNode.insertBefore(elem, this);
                case "AFTERBEGIN":
                    return this.insertBefore(elem, this.firstChild);
                case "BEFOREEND":
                    return this.appendChild(elem);
                case "AFTEREND":
                    return this.parentNode.insertBefore(elem, this.nextSibling);
                default:
                    throw new Error("Invalid element position `" + position + "`");
            }
        }
    });
}

safari8 safari10 对于父元素是border-radius,overflow:hidden;

子元素可以被父元素相切,只显示父元素相切后的内容, 但是子元素如果使用了keyframe帧动画,则出现bug,相切的并不是border-radius后的结果.

IE

word-wrap: break-word; 文字换行, Chrome,Safari,Firefox 无论中英文,还是超长英文单词,都能够自动换行不溢出.

但是IE10,IE11对于超长单词仍然溢出,需要添加word-break: break-all;

chrome 31 or 搜狗5.0.9 (2014年)

chrome 31 内核 bug , supports 函数不能赋值给其他变量执行

var support = (window.CSS && window.CSS.supports) || window.supportsCSS || false;
if(support){
    return support('-webkit-text-decoration-style','wavy')||support('text-decoration-style','wavy');
}
return false;

如上,会报TypeError:Illegal invocation

可修改为如下

 if(window.CSS&&window.CSS.supports){
    return window.CSS.supports('-webkit-text-decoration-style','wavy')||window.CSS.supports('text-decoration-style','wavy');
}else if(window.supportsCSS){
    return window.supportsCSS('-webkit-text-decoration-style','wavy')||window.supportsCSS('text-decoration-style','wavy');
}else{
    return false;
}

IE ajax get 缓存

IE 发送 GET 请求的缓存策略比较激进

当服务端没有设定相关缓存策略时,IE是默认缓存GET请求的。下次同地址的GET请求将会直接 200 from cache

在使用 axios 发送请求时,我们需要申明禁用缓存,表明请求该地址不使用本地缓存。

IE11 禁用ajx get 缓存


const headers = {
    'X-Requested-With': 'XMLHttpRequest',
    'Pragma': 'no-cache',
    'Expires': 0
};

export const httpSys = axios.create({
    baseURL:baseURLSys,
    timeout: 60000,
    withCredentials: true,
    headers,
});

polyfill service

为兼容IE9,IE10,IE11等等众多浏览器,

我们需要在自己的代码工程里引入非常多的 polyfill,

但大部分 polyfill 对于新版的Chrome,Firefox 都是不必要的,但是也被打包到bundle里,造成bundle包很大

使用 polyfill service 我们可以免除对于polyfill的考虑,并且每个浏览器各自下载的内容不同,不需要的不会被下载。

并且基于CDN和缓存,各浏览器基本只需要下载一次。

并且会自动解决各种polyfill依赖问题,例如 https://cdn.polyfill.io/v2/polyfill.js?features=Element.prototype.closest|always

实测 EDGE16 不支持 Promise.prototype.finally

https://hospodarets.com/promise.prototype.finally#support

node >= 8.1.4 <10.0.0 可以通过 node -harmony-promise-finally 开启 Promise.prototype.finally

node >=10.0.0 默认支持 Promise.prototype.finally

https://cdn.polyfill.io/v2/polyfill.min.js?features=default,Promise.prototype.finally

https://polyfill.io/v2/docs/

https://babeljs.io/docs/usage/polyfill/ babel-polyfill包含的core-js 也是一种方式

异步之中的下载文件

在异步中使用window.open是无法生效的

发现异次元的下载链接

使用 form.submit 向后端提交下载地址 (不要使用target _blank) ,后端302即可实现异步调用文件下载

还可以使用定时器设定打开任务,然后ajax回调后设定变量

window.opener

window.open 打开同源网站,目标站点可访问 window.opener, 且可操作其opener各种属性和方法

对应打开的非同源网站, 目标站点不能读取 opener的信息,如 location.href. 出现跨域问题,但是可以调用 window.opener.location.replace/assign 等 , 让原网站跳转指定url.

判断是否在某个div内

在jQuery中,我们可以直接使用closest从此元素向上查找父元素,判断包含关系,

在原生JS中可以使用如下代码

function isParent(node, parent) {
  while (node) {
    if (node === parent) {
      return true;
    }
    node = node.parentNode;
  }
  return false;
}

下面一个函数更加灵活,使用自定义函数判断是否查找到。

var closest = function(el, fn) {
    return el && (fn(el) ? el : closest(el.parentNode, fn));
}

参考: http://clubmate.fi/jquerys-closest-function-and-pure-javascript-alternatives/

document.querySelector("#outer-3”).closest("#outer-1”)

if (window.Element && !Element.prototype.closest) {
    Element.prototype.closest =
    function(s) {
        var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i,
            el = this;
        do {
            i = matches.length;
            while (--i >= 0 && matches.item(i) !== el) {};
        } while ((i < 0) && (el = el.parentElement));
        return el;
    };
}

参考: http://usefulangle.com/post/44/jquery-closest-function-in-pure-vanilla-javascript

MDN上也有Polyfill

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

query builder


function buildQuery(params){
    var esc = encodeURIComponent;
    return Object.keys(params).map(k => esc(k) + '=' + esc(params[k])).join('&');
}
function buildQuery(params){
    var esc = encodeURIComponent;
    return Object.keys(params).map(function(k) {return esc(k) + '=' + esc(params[k]);}).join('&');
}

function query(o){
	return Object.entries(o).reduce((arr, [k, v]) => arr.concat(encodeURIComponent(k) + '=' + encodeURIComponent(v)), []).join('&')
}

获取查询查询字符串get参数值

function getParam()
{
	var data = decodeURIComponent(location.href).split("?")[1].split("&");
	var param = {};
	for(var i = 0; i<data .length; i++)
	{
		param[data [i].split("=")[0]] = data [i].split("=")[1];
	}
	return param;
}

类似于PHP中的$_GET

var $_GET = (function()
{
	var url = decodeURIComponent(location.href);
	var u = url.split( "?" );
	if ( typeof (u[1]) == "string" )
	{
		u = u[1].split( "&" );
		var get = {};
		for ( var i in u)
		{
			var j = u[i].split( "=" );
			get[j[0]] = j[1];
		}
		return get;
	}
	else
	{
		return {};
	}
})();

给出要获取的参数,每次都会解析一遍

function getPar(par)
{
	//获取当前URL
	var local_url = decodeURIComponent(location.href);
	//获取要取得的get参数位置
	var get = local_url.indexOf(par + "=" );
	if (get == -1)
	{
		return false ;
	}
	//截取字符串
	var get_par = local_url.slice(par.length + get + 1);
	//判断截取后的字符串是否还有其他get参数
	var nextPar = get_par.indexOf( "&" );
	if (nextPar != -1)
	{
		get_par = get_par.slice(0, nextPar);
	}
	return get_par;
}

QueryString

function QueryString(item)
{
	var value = location.search.match(new RegExp('[\?\&]' + item + '=([^\&]*)(\&?)','i'));
	return value ? value[1] : value;
}

Cookie操作

function setCookie(cookiename, cookievalue, hours)
{
	var date = new Date();
	date.setTime(date.getTime() + Number(hours) * 3600 * 1000);
	document.cookie = cookiename + "=" + cookievalue + "; path=/;expires = " + date.toGMTString();
}

取得Cookie中某个键的值

$.getCookie = function(name)
{
	 if(document.cookie.indexOf(name)!=-1) return document.cookie.split(name+'=')[1].split(';')[0]
}

另一个

function getcookie(cookieid)
{
	//获取cookie字符串
	var strCookie=document.cookie;
	//将多cookie切割为多个名/值对
	var arrCookie=strCookie.split("; ");
	var cookieval;
	//遍历cookie数组,处理每个cookie对
	for(var i=0;i<arrCookie.length;i++)
	{
		var arr=arrCookie[i].split("=");
		//找到名称为userId的cookie,并返回它的值
		if(cookieid==arr[0])
		{
			cookieval=arr[1];
			break;
		}
	}
	return(cookieval);
}

类似于 php 中 http_build_query

jQuery 中的 $.param

var params = {
    parameter1: 'value_1',
    parameter2: 'value 2',
    parameter3: 'value&3'
};

var esc = encodeURIComponent;
var query = Object.keys(params).map(k => esc(k) + '=' + esc(params[k])).join('&');

进入全屏

function gofull()
{
	var el=document.documentElement;
	el.webkitRequestFullScreen&&el.webkitRequestFullScreen();
	el.mozRequestFullScreen&&el.mozRequestFullScreen();
	el.requestFullscreen&&el.requestFullscreen();
}

字符串去空格

String.prototype.trim=function() {return this.replace(/(^\s*)|(\s*$)/g,""); }
String.prototype.ltrim=function(){return this.replace(/(^\s*)/g,""); }
String.prototype.rtrim=function(){return this.replace(/(\s*$)/g,""); }

字节格式化

function size(size)
{
	var name=['B','KB','MB','GB','TB','PB'];
	var pos=0;
	while(size>=1204)
	{
	    size/=1024;
	    pos++;
	}
	return size.toFixed(2)+" "+name[pos];
}

判断页面滚动到底部和顶部

$(document).scroll(function()
{
	if ($(document).scrollTop()<=0)
	{
		console.log("滚动条已经到达顶部为0");
	}
	if ($(document).scrollTop() >= $(document).height() - $(window).height())
	{
		console.log("滚动条已经到达底部为" + $(document).scrollTop());
	}
});

滚动到顶部

$('html,body').animate({
    scrollTop: '0px'
}, 800);

纯js滚动到顶部

window.scrollTo(0, 0);

或者

document.body.scrollTop = 0
document.documentElement.scrollTop = 0

平滑滚动

chrome 原生支持

window.scroll({top: 0, left: 0, behavior: 'smooth' });

https://stackoverflow.com/questions/8917921/cross-browser-javascript-not-jquery-scroll-to-top-animation

用JS实现

linear

function scrollToTop(scrollDuration) {
    var scrollStep = -window.scrollY / (scrollDuration / 15),
        scrollInterval = setInterval(function(){
        if ( window.scrollY != 0 ) {
            window.scrollBy( 0, scrollStep );
        }
        else clearInterval(scrollInterval); 
    },15);
}

ease in and out

function scrollToTop(scrollDuration) {
const   scrollHeight = window.scrollY,
        scrollStep = Math.PI / ( scrollDuration / 15 ),
        cosParameter = scrollHeight / 2;
var     scrollCount = 0,
        scrollMargin,
        scrollInterval = setInterval( function() {
            if ( window.scrollY != 0 ) {
                scrollCount = scrollCount + 1;  
                scrollMargin = cosParameter - cosParameter * Math.cos( scrollCount * scrollStep );
                window.scrollTo( 0, ( scrollHeight - scrollMargin ) );
            } 
            else clearInterval(scrollInterval); 
        }, 15 );
}

https://stackoverflow.com/questions/21474678/scrolltop-animation-without-jquery

scroll-behavior: smooth; 也支持锚点跳转

https://stackoverflow.com/questions/17631417/css-pure-css-scroll-animation

运行JS代码

// execute code in the content page scope
var injectScript = function(source) {
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.textContent = ';(' + source.toString() + ')()';
    document.body.appendChild(script);
    document.body.removeChild(script)
}

添加CSS代码

// insert css
function injectStyle(css) {
    var script = document.createElement('style')
    script.setAttribute('type', 'text/css')
    script.textContent = css
    document.head.appendChild(script)
}

利用注释,可也生成html,css,js 等代码

(function(){/* body{background:#f00} */}).toString().slice(15,-4)

当然利用es6的字符串模板更加方便,http://babeljs.io/repl/ 在线转换为es5代码

一些正则验证

验证邮箱

function is_mail(str)
{
	return(new RegExp(/^[0-9a-zA-Z]+@(([0-9a-zA-Z]+)[.])+[a-z]{2,4}$/i).test(str));
}

验证手机号

function is_tel(str)
{
	return(new RegExp(/^1[34578][0-9]{9}$/).test(str));
}

Base64的1x1像素间隔gif

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">

哪个个深拷贝最快

http://jsben.ch/bWfk9

Object.assign 并不是深拷贝

一个range函数

function range(start, count)
{
	return Array.apply(0, Array(count)).map(function (element, index)
	{
		return index + start;
	});
}

简化console.log

var log = console.log.bind(console);

简化querySelector

srcQuery=srcQ.querySelector.bind(srcQ);

生成较短的唯一ID

一定年限内单线程非连续调用唯一

function shortId()
{
	var time=Date.now()+'';
	time=parseInt(time.substr(1));
	return time.toString(36);
}

生成随机短ID,仅具备较低的不重复性

var t=Math.floor((1+Math.random())*0x1000000000).toString(16);

生成指定长度随机字符串 PHP代码

function code($nc, $a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
{
    $l=strlen($a)-1; $r='';
    while($nc-->0) $r.=$a{mt_rand(0,$l)};
    return $r;
}

给jQuery的form添加serializeObject

jQuery.prototype.serializeObject=function()
{
	var a,o,h,i,e;
	a=this.serializeArray();
	o={};
	h=o.hasOwnProperty;
	for(i=0;i<a.length;i++)
	{
		e=a[i];
		if(!h.call(o,e.name))
		{
			o[e.name]=e.value;
		}
	}
	return o;
};

使用效果如下

jQuery("form").serialize(); //"username=&password="
jQuery("form").serializeArray(); //[{name:"username",value:""},{name:"password",value:""}]
jQuery("form").serializeObject(); //{username:"",password:""}

jQuery 返回顶部

$('html').animate({scrollTop: '0px'}, 800);

safari 中需要使用 $(‘body’)

因此使用下面代码更加兼容

$('html,body').animate({scrollTop: '0px'}, 800);

直接下载blob数据

function downFile(blob, fileName)
{
    if (window.navigator.msSaveOrOpenBlob)
    {
        navigator.msSaveBlob(blob, fileName);
    }
    else
    {
        var link = document.createElement('a');
        link.style.display='none';
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        document.body.appendChild(link);
        link.click();
        setTimeout(function()
        {
            document.body.removeChild(link);
            window.URL.revokeObjectURL(link.href);
        },200);
    }
}

兼容IE,Chrome,Firefox,Safari,注意 appendChild 这一步不能缺,否则Firefox不兼容

var blob = new Blob([res.data], {type: "application/vnd.ms-excel"}),
    fileName = '下载.xls';
downFile(blob, fileName);
fetch('/files/bin/http.xz').then(res=>res.blob()).then(res=>{console.log(res);downFile(res,"http.xz")})

iso 8 html5 canvas 限制

https://stackoverflow.com/questions/18565395/why-does-canvas-context-drawimage-fail-on-iphone

ios 的canvas 有像素限制,由于像素过大,实测拍出来的图片不能使用 canvasContext.drawImage处理.

现象是canvas画出来的图片为空白,toBlob和toDataUrl也不能取到该canvas的数据.

解决方案

https://github.com/stomita/ios-imagefile-megapixel (未测试)

还有更加全面的方案

https://github.com/blueimp/javascript-load-image

加上 https://github.com/blueimp/JavaScript-Canvas-to-Blob

能更加方便的处理图片,实测可以解决拍出的图片过大无法处理的问题.

https://blueimp.github.io/JavaScript-Load-Image/ 提供在线检查Exif meta data

资源预取

function prefetch(url)
{
	var link = document.createElement("link");
	link.href = url;
	link.rel="prefetch";
	document.head.appendChild(link);
}

Google Analytics统计JS错误

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
window.addEventListener('error', function (err)
{
	ga('create', 'UA-xxx-1', 'auto');
    var lineAndColumnInfo = err.colno ? ' line:' + err.lineno +', column:'+ err.colno : ' line:' + err.lineno;
    ga('send', 'event', 'JavaScript Error', err.message, location.href+' => '+err.filename + lineAndColumnInfo + ' -> ' +  navigator.userAgent, 0, true );
});

UA-xxx-1使用自己的统计ID

jQuery并发发送ajax

$.when($.get('assets/misc/1.json'), $.get('assets/misc/2.json')).then(function(r1, r2)
{
    console.log(r1[0].message + " " + r2[0].message);
})

微信内JS获取地理位置

<script type="text/javascript" src="https://apis.map.qq.com/tools/geolocation/min?key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77&referer=mywxapp"></script>

获取精确定位信息
var options = {timeout: 9000};
geolocation.getLocation(showPosition, showErr, options);

粗糙定位信息
geolocation.getIpLocation(showPosition, showErr)

Fetch跨域带cookie

fetch(url, {credentials: 'include'})

jQuery ajax 跨域带 cookie

$.ajaxSetup({xhrFields: {withCredentials: true }});

google closure-compiler

编译好的jar下载

http://dl.google.com/closure-compiler/compiler-latest.zip
http://dl.google.com/closure-compiler/compiler-latest.tar.gz

解压后包含一个jar文件

或者 https://github.com/google/closure-compiler 下载源码与查看说明

https://developers.google.com/closure/compiler/docs/api-ref

使用

java -jar compiler.jar --js_output_file=out.js in1.js in2.js in3.js ...
# Recursively include all js files in subdirs
java -jar compiler.jar --js_output_file=out.js 'src/**.js'

java -jar closure-compiler-v20180402.jar --js_output_file 1.js --language_out NO_TRANSPILE -O WHITESPACE_ONLY --strict_mode_input false yarn-1.6.0.js