【JS教程】JS实战问题篇

1、关于scrollTop 的问题

    只有在容器处于显示状态时,重设scrollTop才会生效。否则将无效。

2、关于cookie路径的问题

    存储cookie时需要注意路径的问题。即使用jquery.cookie插件时,一定要注意第三个参数的设置。

3、关于IE6之Fixed定位问题

    众所周产于公元2002年、且当前为我国主流的IE6浏览器是不支持CSS2的静止定位属性fixed,蛋疼的前端工程师们为此发明了各种形式的解决方案:

    一、常规js解决之道

    这个方案最为古老,比IE6还老,且应用十分广泛:比如很多跟着滚动条走的对联广告就是使用此方案。缺点就是拖动滚动条元素抖动很厉害,虽然通过平滑处理可以改善下,效果仍然不理想。不过要说的是此方案虽然视觉效果差了那么一点,稳定性与可控性没得二话说的。

    二、动用HTML结构与布局模拟法

    此方案曾经被163博客应用,163把所有的内容放在一个高度100%且滚动条设置为自动的容器中,然后再下面设置一个绝对定位的层,这样这个绝对定位的层就可以达到静止状态。原理:你拖动的滚动条并不是拖动的整个页面,而是那个模拟整页的容器,所以容器外的地方都是“静止”的。详细:http://bbs.blueidea.com/thread-2930592-1-1.html

    这里视觉效果达到完美,问题有三:

  • 1、需要改变HTML结构

  • 2、破坏了用户体验:刷新页面之后滚动条不会停留在原处

  • 3、破坏js一些事件,如cwindow的scroll事件

    三、、老技术新用的expression加fixed背景方案

    此方案能够视觉上完美的实现静止定位。例子:

    这两种方案本来已经很完美了,我artDialog早期版本也是这么实现的,可是后来使用过程中发现了一个更加不能容忍的BUG,我在回复cloudgamer写了BUG触发DEMO

    这个问题的本质就是用expression模拟fixed外包裹元素实际是设置了absolute,并且遮盖了页面,IE6可能导致其下页面一些元素无法响应事件,如div、td、span等,只有a、button、input等元素可以响应,那些无法响应事件的元素如果包含了文字BUG又会消失。

    下面的DEOM代码是我四天前写好的,直到今天才在自己Blog上分享,希望彻底埋了第上述第四方案的坑了:

<!DOCTYPE>
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>ie6 Fixed</title>
  </head>

<body>
  <p><button id="fixedBtn">设置静止定位</button><button id="fixedBtn2">改变位置</button></P>
  <P><button id="absoluteBtn">设置绝对定位</button><button id="absoluteBtn2">改变位置</button></P>
  <div id="fixed" style="width: 60px; height:60px; background:#C0C0C0; border:solid 1px #000;"></div>
  <div style="height:1400px;" title="请拖动滚动条"></div>
  <script type="text/javascript">
  var position = function(){
      var isIE6 = !-[1,] && !window.XMLHttpRequest,
         html = document.getElementsByTagName('html')[0],
          dd = document.documentElement,
          db = document.body,
          dom = dd || db,
          // 获取滚动条位置
          getScroll = function(win){
              return {
                  left: Math.max(dd.scrollLeft, db.scrollLeft),
                  top: Math.max(dd.scrollTop, db.scrollTop)
                  };
          };
      // 给IE6 fixed 提供一个"不抖动的环境"
      // 只需要 html 与 body 标签其一使用背景静止定位即可让IE6下滚动条拖动元素也不会抖动
      // 注意:IE6如果 body 已经设置了背景图像静止定位后还给 html 标签设置会让 body 设置的背景静止(fixed)失效
      if (isIE6 && document.body.currentStyle.backgroundAttachment !== 'fixed') {
          html.style.backgroundImage = 'url(about:blank)';
          html.style.backgroundAttachment = 'fixed';
      };

      return {
          fixed: isIE6 ? function(elem){
              var style = elem.style,
                  doc = getScroll(),
                  dom = '(document.documentElement || document.body)',
                  left = parseInt(style.left) - doc.left,
                  top = parseInt(style.top) - doc.top;
              this.absolute(elem);
              style.setExpression('left', 'eval(' + dom + '.scrollLeft + ' + left + ') + "px"');
              style.setExpression('top', 'eval(' + dom + '.scrollTop + ' + top + ') + "px"');
          } : function(elem){
              elem.style.position = 'fixed';
          },

          absolute: isIE6 ? function(elem){
              var style = elem.style;
              style.position = 'absolute';
              style.removeExpression('left');
              style.removeExpression('top');
          } : function(elem){
            elem.style.position = 'absolute';
          }
      };
  }();
  </script>
  <script>
  var elem = document.getElementById('fixed');
  document.getElementById('fixedBtn').onclick = function(){
      elem.style.left = '100px';
      elem.style.top = '100px';
      position.fixed(elem);
  };
  document.getElementById('fixedBtn2').onclick = function(){
      elem.style.left = '400px';
      elem.style.top = '100px';
      position.fixed(elem);
  };
  document.getElementById('absoluteBtn').onclick = function(){
      elem.style.left = '100px';
      elem.style.top = '100px';
      position.absolute(elem);
  };
  document.getElementById('absoluteBtn2').onclick = function(){
      elem.style.left = '400px';
      elem.style.top = '100px';
      position.absolute(elem);
  };
  </script>
  </body>
  </html>

    四、使用js 设置 expression 与 removeExpression 法

    在实现了ie6 fixed的前提下,实际应用中可能还需要对这个fixed元素调整位置,如鼠标拖拽元素。上面的DEMO同样是使用expression实现,不同的是expression直接应用到了要操作的对象上,这样就不会发生上述BUG了,直接设置在元素上后再想手动改变元素位置(如拖动)是相当困难的,必须有一个可以重置expression的方法,而前些天在msdn上看到的removeExpression方法,问题迎刃而解!你也可以看artDialog3在IE6 fixed的表现

    如果光说到标准的fixed定位除了left与top之外还有right与bottom属性,显然目前此方案支持它们会比较麻烦,还好就是这两个属性在javascript组件中很少用到。

4、关于循环中闭包的问题

    在项目中经常遇到for循环中返回函数的问题,每次用的方法很杂,总结一下~

    问题引入:

var li=document.getElementsByTagName("li"); 
 for(var i=0;i<li.length;i++){ 
     li[i].onclick=function(){alert(i);} 
 }

    闭包允许你引用父函数中的变量,但提供的值并非该变量创建时的值,而是在父元素范围内的最终值。

    解决方案一: 用一个匿名函数包含语句块,同时传入一个参数后执行,传如参数就成了局部变量。

for(var i=0;i<li.length;i++){    
    (function(index){     
        li[index].onclick=function(){
            alert(index);
        }
    })(i); 
}

    解决方案二: 通过dom元素绑定属性来记录下标。

for(var  i = 0,len = lists.length;i < len;i++){
    lists[i].j = i;//通过dom的属性绑上j属性,即是i的索引,从而j就记录下了j
    lists[i].onclick=function(){
        alert(this.j); //直接访问其dom属性
    };
}

    解决方案三:调用匿名函数自身的保存的i属性的变量。

var  lists = document.getElementsByTagName("li");   
for(var  i = 0,len = lists.length;i < len;i++){
    (lists[i].onclick =function(){
        alert(arguments.callee.i);//匿名函数调用自身的属性的i
    }).i=i;
}

    解决方案四:再加一层闭包,返回一个函数作为响应事件。

for(var  i = 0,len = lists.length;i < len;i++){
         lists[i].onclick = (function(j){   
             return  function(){//响应事件的函数,参数i作为j,这样i就成了响应事件的局部变量
                 alert(j);
             }
         })(i);
     }
}

5、关于with的两个困惑

    ’use strict‘ 模式下,with直接被禁用!

    【with】:(对象闭包),所谓对象闭包是指使用with语句时与with()所指示对象相关的闭包,该闭包被动态创建并添加到执行环境当前的闭包链顶端。-----摘自《javascript语言精髓与编程实践》4.6.4.2

    【愚见】:with的好处是可以很方便的增删改对象中的属性。问题是,遇到变量冲突和双重闭包就悲剧了。至于网上各位大牛所说的性能问题,我...真心表示不懂。

    【示例】:

    实例一:

    根据周老师书中所言,大意是,因为abc()的函数闭包位于obj的对象闭包中,因此abc()中方位value时应该通过闭包链来存取到obj.value。但事实上,abc()函数的闭包链早在语法分析器就决定了,它被添加在全局闭包之后。因此,abc()的函数闭包与obj的对象闭包并列,obj对象闭包中将无法获取obj.value的值。更无法改变它。

    预期打印结果:

    obj.value: 100 

    value: 2000

    实例二:

    为解决实例一无法获取对象中value值的问题,将代码进行修改,即实例二所示代码。将对象闭包中的函数闭包改为匿名函数。这样可以修改对象obj1.value1,而不会修改value1。

    预期打印结果:

    obj.value: 100 

    value: 2000 

    // 实例一 

var obj = {value : 100};
    var value = 1000;
    with(obj){
        function abc(){
            value *= 2;
        }
        abc();
    }
    console.info('obj.value: ' + obj.value);
    console.info('value: ' + value);
    console.info("---------------------------------------");

    //实例二

    var obj1 = {value : 100};
    var value1 = 1000;
    with(obj1){
        void function(){
            value1 *= 2;
        }();
    }
    console.info('obj1.value: ' + obj1.value);
    console.info('value1: ' + value);

    最后测试结果:

225958_3ppm_565012.png

    safari与chrome同核,结果一样,就不贴了。三个浏览器都是到现在这个时间为止的最新版本,不想截版本信息没有意义。不过ie10能支持console.info,真让我感动的痛哭流涕。

    实例三:

    var obj = new Object();
    obj.value = 100; 
    console.info("befor changed:");
    console.info(obj);
    console.info(obj.value);
    with(obj){
        value += 100;
    }
    console.info("after changed:");
    console.info(obj);
    console.info(obj.value);

    最后测试结果:(浏览器同上一样)

225958_VFIc_565012.png

    这里最让我不解的是,三个浏览器对object.value改变前后的输出结果,都是一样。但是,在chrome中,with物理位置之前,直接输出object对象时,它里面的属性value值是改变之后的值。看图可以很明显看出。但是,在中间的ff中,就不一样了。很费解。ie10没法直接打印出对象内部属性,不知道详情。

    但是ff和chrome的差异让我很不理解。不清楚这个程序到底是怎么执行的。

    chrome中。按我的理解是,因为对象本身就是一个引用,所以,输出对象时,里面的值自然是最终改变后的值,这跟输出的物理位置是无关的。而object.value就是一个具体的值了,它受到代码运行物理位置的影响,所以看起来就跟object不同步了。但是,ff中的现象又改怎么理解呢?

    PS:刚才把ff升级到最新5.0版本,然后发现下面截图所示内容。

225958_aA3e_565012.png点击红色框框,进入到对象中

225958_5smS_565012.png

    结语:那就是说,我的理解没有错,只是ff在打印的时候,记录了一个object的临时状态,但是如果展开object对象,它显示的将是被引用值的最后状态。

6、不同浏览器和设备加载本地xml文件问题

    在本地测试时,IE各版本,safari,ff都能使用jquery的ajax来进行加载,但是在chrome中不行。在chrome中只能用ip地址来访问才能对xml进行正常加载,即必须将网页放到服务器上,用ip才能看到加载后的效果。可将文件放到本地虚拟服务器中,用localhost来访问调试。

7、 this 的指向问题

    注册事件,当事件被触发之后,函数或方法中的this指向已经发生改变,所指为事件触发的对象。这个其实是常识,不过容易忽略,给调试造成麻烦。因此,只要是函数中需要进行事件注册,那么,在事件注册之前,最好先创建一个局部变量,把this赋给该局部变量。然后在事件的callback中需要引用this的地方,引用该局部变量。(切记切记!)

    在事件的callback中运行某方法时,必须使用call或apply,将scope传递进去!(详见南商main.js)

    在提示方法未定义式时,首先最好是直接在引用该方法的代码前一行对this进行console.info(this)。

8、关于 console.info(); 语句前后的注释问题

    写在 console.info(); 语句之前或之后的注释,会直接打印在浏览器中

9、关于匿名函数的书写问题

    以下几种书写格式都正确:

  • Void操作符:用void操作符去执行一个没有用圆括号包围的一个单独操作数。

    void function(){}() 

  • 函数字面量:首先声明一个函数对象,然后执行它。

    (function(){})()  如果漏掉最外层的括号,将提示语法错误!

  • var a = function(){}()    (⊙o⊙)…这个算隐形匿名了,哈哈

  • 优先表达式:由于Javascript执行表达式是从圆括号里面到外面,所以可以用圆括号强制执行声明的函数。

    (function(){}())


转载于:https://my.oschina.net/maomi/blog/199216

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值