Category Archives: JavaScript

Javascript Array sort 在不同浏览器中的不同表现

昨晚发现了一个让我无法理解事情, 看下面的代码:

var x = [{id:1, val:1}, {id:1, val:32}, {id:1, val:42}];
x.sort(function(a, b){
  console.log(a.val, b.val, a.id <= b.id)
  if (a.id <= b.id){
    return 1;
  }else if (a.id > b.id){
    return -1;
  }
});
console.log(x.map(function(obj){return obj.val;}).join(','));

在IE已经Edge, 1.9.8版本的phantomjs里给出来的结果是: 1,32,42
而在Firefox, Chrome, Nodejs 下给出的是: 42,32,1
在比较函数里插入了一行代码发现了问题所在:

var x = [{id:1, val:1}, {id:1, val:32}, {id:1, val:42}];
x.sort(function(a, b){
  console.log(a.val, b.val, a.id <= b.id)
  if (a.id <= b.id){
    return 1;
  }else if (a.id > b.id){
    return -1;
  }
});
console.log(x.map(function(obj){return obj.val;}).join(','));

c

明显看出IE里a跟b的顺序与其他浏览器里不一样, IE里面是反的, 导致在这种特殊情况下结果的不一致, 看一下msdn与mdn上对于这个的不同描述:
mdn
m

感觉还是mdn上的更好理解, 而且Chrome跟Firefox参数的传入顺序似乎也更符合我们的心理预期.
如果排序过程中打印数值长度, 微软系列的竟然是0. 这也很特别.

比AngularJS 更简单好用的前端框架Aurelia

AugularJS我用得并不多,但是,我的感觉是学习曲线挺陡峭的,需要理解他的那一堆的概念。在试图解决一个问题时引入了太多更复杂的东西,有点让人怀疑这样做是否值得。另外,Angular自己不包含模块加载的功能,需要依赖其他的库,比如requirejs。不过这样一来,angular的代码就更不清晰了,一层套一层,到处都是花括号,哪边要写错了,就头大了。
相比AngularJS,这个由前AngularJS开发团队成员开发的新的前端框架Aurelia就显示出很多优势:
1. 使用Typescript或者ES6/ES7的语法,这样让代码的可读性简洁度都有明显的提高;
2. 通过SystemJS等库提供默认的模块加载支持,比起requirejs等,这种加载模式更简单;
3. 省去了AngularJS的很多概念,很容易上手,一切代码都显得那么得优雅;
4. 对其他JS库的支持非常好, 尤其是很多nodejs下的库也可以拿过来用。

当然也有一些劣势:
1. 社区还没有AngularJS的大;
2. 由于使用了很多html5新的特性,导致对浏览器的兼容性支持较差。

Aurelia官网:http://aurelia.io/

为了性能,请用Canvas而不是IMG

前些日子做了一个网站,是自适应式的网站(也有人叫响应式),带视差效果,大量的使用图片,并且我们做的网站一般考虑屏幕大小达到1920,以保证大屏浏览者依然能够看到清晰的图片。

于是问题就来了,自适应暂不说,只说这视差效果,要求在滚动页面时,页面里面绝大多数的图片能有一个位移或者旋转这样的动画,意味着要想浏览器达到60帧的帧频,程序必须质量够高,浏览器渲染页面的性能也要够高。

几个浏览器下测试做出来的页面,出人意料,一直认为性能最高的Chrome出现了问题,在滚动页面时帧频降到了10FPS以下,反而IE9+的性能相当高,尤其是IE10,非常流畅。问题出在了哪?在经过反复的测试后,终于发现Chrome下性能低的原因。

下面是在Chrome的调试工具里的看到了,很明显在我滚动页面时,IMG标签被多次解码,而这对性能的影响是非常明显的,尤其是页面中存在大量大尺寸图片时。

chrome devtools

 

在我使用了Canvas代替了IMG之后,这种图片重复解码的问题就不存在了,而且性能提升非常明显,基本上能达到最大帧频。不过IE下Canvas似乎难以做自动等比缩放,所以IE下我继续使用IMG,正好IE下图片不存在这样的问题。

 

避免网页中直接使用IMG标签

为什么要在网页中避免网页中直接使用IMG标签?我总结一下有以下几个原因:

1、无法控制图片加载的时间,比如我做一个有大量图片的网站,而网站中的图片可能还有CSS动画什么的,我们需要像以往Flash网站一样的加载进度条,在所有图片加载完后才能正常让网页动起来。如果不是这样,可能动画就达不到预期的效果,像CSS动画有可能就会出现衔接的问题,因为浏览器是在某图片加载完后对它做动画,而并不是在所有图片加载完后做动画,这就会导致动画不同步的问题。

2、无法在某些时候换用其他的显示图片的方式,比如有时我们想用前景的方式显示图片,有时我们想用Canvas的方式显示图片,如果写死用IMG标签,那会导致这样的功能就难以实现了。

3、这个问题跟第一个问题有几分相似,如今很多网站已经采用自适应(响应式)的设计,对于这种设计,会出现某些资源在某些设备上并不想显示,不仅是不想显示,甚至是不能加载,想象一下DESKTOP下的页面用的大图我们万一用手机访问,这张图依然被加载,那轻则网页打开缓慢,重则浏览器崩溃,万一浏览者不是用的WIFI,那。。。他死定了。但是正如第一点所说,没办法直接控制IMG标签,当然有人可能会说了,我用JS来判断,然后用JS来插入这些图片进页面不也可以么,嗯。。。是可以,不过你可以试试看,看这种写法会浪费你多少时间,万一要后期要修改呢?不仅这种方式不直观,而且还会浪费大量的时间。

4、性能问题,某些浏览器(比如Chrome)对于图片处理或许会存在一定的性能问题,在滚动页面或者重新渲染页面时,某些图片会存在即时解码的问题,也就是即便之前已经解码了也渲染了,当页面有改动或者滚动需要重新渲染时,这张图片会被重新解码,之后是RESIZE,之后再显示,如果对页面性能要求高,比如在做动画时,那这是致命的性能问题。

好吧,暂时我也就发现这多问题了,那么解决方法是什么?这是我现在常用的方法:

1、使用DIV来代替图片,比如我们可以这样写:<div class=”picture” data-src=”a.jpg”></div>。这里我们使用一个固定的Class名称来代表这是一张图片,之后使用HTML的DATASET,设置一个自定义的属性data-src来指明图片的路径。

2、使用JS来获取页面中的带picture标签的DIV,之后加载相应的图片,然后可以在这些图片都加载完后插入到相应的DIV中,之后显示页面。这样可以灵活解决图片加载的问题。

3、对于上面据说的可能像Chrome这样的浏览器存在的性能问题,我们可以使用Canvas来代替IMG标签,这样性能问题也解决了。

关于Photoshop中图层的批量导出

最近又遇上了一个项目,需要把Photoshop中的各层全部导成相应的图片,并且要获取相应的图片在Photoshop中的实际位置的信息,用于在网页中重新生成与Photoshop中一样的整图的效果,于是整了一个JSX的脚本,似乎还挺有用处的,可以将photoshop中相应层中的图片先裁切掉多余的空白,之后将它们保存,并且最终生成一个xml文档或者是json的文档将相应的图像位置信息保存在其中。

不过还有几点问题:

1. PSD中不能有重名的层,重名的层中的图片会被相互覆盖。

2. PSD中不要有空层,有可能会引起错误。

时间紧,也没时间去完善,只能将就一下了。

另外直接用这个在图片出现重叠时会比较麻烦,好在有另一个强大的软件:TexturePacker(http://www.texturepacker.com),这个软件可以在Mac,Windows,Ubuntu等多个主流操作系统上运行, 可以从SWF或者直接将多张图合并成一张纹理图,同时可以生成相应格式的数据,包括JSON格式的,并且这个软件对于有技术类博客的网友,可以免费提供注册证书,无需花银子了,我的就是用的免费的,软件作者还很友好的,配合这个工具,再多的图也可以轻松排了。

腾讯图片加载的诡计

今天偶尔在腾讯上看八卦新闻来着, 注意到了一个奇怪的现象, http://ent.qq.com/a/20101011/000085.htm#p=11 诸如此类的带图片的新闻, 在加载其他张图片时是会显示加载进度的, 但是右键图片发现此处并非flash所做, 真是奇怪了, 难道腾讯在此处没有用flash, 而直接能过javascript来加载图片并且获取到图片加载进度? 感觉不太可能, 在网上找了一阵也没发现用纯javascript实现的这种效果, 于是下载整个网页来看看腾讯到底怎么整出来了. 这一看终于明白了, 原来一切只是表象而已, 事实上还是flash完成了加载.

直接另存这个页面, 在相应的文件夹下有一个叫hd_min_v1.js的文件, 还好里面的变量没被压缩, 清晰好认, http://javascript.about.com/library/blformat.htm 直接复制到这个网页, 格式化一下, 好认点, 可以看到里面有一个loadingProcess的对象, 内部有一个progressPicHandler的方法, 里面完成了对进度的计算并且写入到一个loading的html元素里:

但是再用ultraedit搜索整个文件夹你也看不到哪边调用了这个方法, 这时回过头来看代码, 里面的init方法中有嵌入一个flash, 所以猜测还是flash里调用了这个方法, 找到文件夹中的loadingAs3.swf这个文件, 通过工具反编译, 终于在里面发现了调用这个方法的代码:

哎, 整活了半天, 才知道原来腾讯也只是通过js向swf发送加载图片请求, 而在swf加载图片的时候swf在侦听加载进度的方法中再次调用js, 完成将进度写到html里, 转了一圈, 终于回来了.

Jquery UI Dialog 无法提交服务器解决方法

Jquery UI 库中的 Dialog 控件里如果存在表单,是无法直接提交到服务器的,因为在jquery.ui.dialog.js文件中(1.8.2版本中大概是在59行的位置)有这样的代码:
[javascript]
uiDialog = (self.uiDialog = $(”))
.appendTo(document.body)
.hide()
.addClass(uiDialogClasses + options.dialogClass)
.css({
zIndex: options.zIndex
})
// setting tabIndex makes the div focusable
// setting outline to 0 prevents a border on focus in Mozilla
.attr(‘tabIndex’, -1).css(‘outline’, 0).keydown(function(event) {
if (options.closeOnEscape && event.keyCode &&
event.keyCode === $.ui.keyCode.ESCAPE) {

self.close(event);
event.preventDefault();
}
})
[/javascript]
也就是说被创建的dialog元素是被添加到body元素中了,你想dialog的内容被作为子元素添加在了它所创建的这个dialog元素里,自然也会被从原始的form元素中拿出来了,这样它里面的表单元素也就无法知道自己提交到哪去了。

解决方法:
有人可能想直接修改上面这段代码,把.appendTo(document.body)改为类似.appendTo(document.forms[0])这样的代码。不过不管怎么说修改jquery这么成熟的库总不是明智的作法。我个人是在调用了dialog()方法后再将生成的元素放入到form中。
例如:
[javascript]
$(‘#control_panel_dialog’).dialog();
$(“body>div[role=dialog]”).appendTo(‘form:first’);
[/javascript]
或者:
[javascript]
$(‘#control_panel_dialog’).dialog().parent().appendTo(‘form:first’);
[/javascript]
或者:
[javascript]
$(‘#control_panel_dialog’).dialog();
$(‘.ui-dialog’).appendTo(‘form:first’);
[/javascript]
由于我正常是使用asp.net,正常只有一个 form 元素,这样的话直接加入到 ‘form:first’ 就好了。

Get viewport size (width and height) with javascript


Javascript 获取窗口宽度和高度值

获取页面高度,窗口高度,滚动条高度等参数值:

  1. function getPageScroll(){
  2. var yScroll;
  3. if (self.pageYOffset) {
  4. yScroll = self.pageYOffset;
  5. } else if (document.documentElement && document.documentElement.scrollTop){   // Explorer 6 Strict
  6. yScroll = document.documentElement.scrollTop;
  7. } else if (document.body) {// all other Explorers
  8. yScroll = document.body.scrollTop;
  9. }
  10. arrayPageScroll = new Array(”,yScroll)
  11. return arrayPageScroll;
  12. }
  13. function getPageSize(){
  14. var xScroll, yScroll;
  15. if (window.innerHeight && window.scrollMaxY) {
  16. xScroll = document.body.scrollWidth;
  17. yScroll = window.innerHeight + window.scrollMaxY;
  18. } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
  19. xScroll = document.body.scrollWidth;
  20. yScroll = document.body.scrollHeight;
  21. } else { // Explorer Mac…would also work in Explorer 6 Strict, Mozilla and Safari
  22. xScroll = document.body.offsetWidth;
  23. yScroll = document.body.offsetHeight;
  24. }
  25. var windowWidth, windowHeight;
  26. if (self.innerHeight) {  // all except Explorer
  27. windowWidth = self.innerWidth;
  28. windowHeight = self.innerHeight;
  29. } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
  30. windowWidth = document.documentElement.clientWidth;
  31. windowHeight = document.documentElement.clientHeight;
  32. } else if (document.body) { // other Explorers
  33. windowWidth = document.body.clientWidth;
  34. windowHeight = document.body.clientHeight;
  35. }
  36. // for small pages with total height less then height of the viewport
  37. if(yScroll < windowHeight){
  38. pageHeight = windowHeight;
  39. } else {
  40. pageHeight = yScroll;
  41. }
  42. if(xScroll < windowWidth){
  43. pageWidth = windowWidth;
  44. } else {
  45. pageWidth = xScroll;
  46. }
  47. arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight)
  48. return arrayPageSize;
  49. }