个人博客

A web front-end programmer's personal blog.

整理项目中遇到的一些问题

####前段时间做了驴妈妈的微游记项目,整理一部分小技巧和感觉比较坑的问题,希望以后再碰到类似的可以更好更快的解决。

######项目在线地址入口,请点击这里

####1、限制和截取文字问题

如果想在文本框中输入文字,并随时统计输入的字数(这里两个英文字母算作一个字符,一个汉字算一个字符),而且还要注意通过鼠标复制粘贴操作的情况,所以只能通过监听 keyup 与input propertychange两个事件来完成,之后的编辑页面由于要对动态生成的元素进行输入监听,众所周知jQuery有个live方法(这里指1.7之前的),可这几个事件在IE8下的live方法中不起作用,于是想了个小idea,将其封装成一个方法,每次动态创建元素后调用一下,就跳过了IE8的问题。

function textCounts(){
    $('.textareaDiv textarea').bind('keyup input propertychange',function(ev){ 
        var len = getLength($(this).val()); 
        len = Math.ceil(len/2);
        if(len>1000){ 
           $(this).val(sub($(this).val(),2000)); 
           $(this).parent().siblings('.writeTitle').find('.remainTxt span').html(1000); 
        }else{ 
           $(this).parent().siblings('.writeTitle').find('.remainTxt span').html(len); 
        } 
    });
}
//截取指定长度字符
function sub(str,n){ 
    var r=/[^\x00-\xff]/g; 
    if(str.replace(r,"mm").length<=n){return str;} 
        var m=Math.floor(n/2); 
        for(var i=m;i<str.length;i++){ 
            if(str.substr(0,i).replace(r,"mm").length>=n){ 
            return str.substr(0,i); 
        } 
    } 
    return str; 
}
// 统计字符长度
function getLength(str){
    return String(str).replace(/[^\x00-\xff]/g,'aa').length;
}

####2、日历问题

项目一开始使用了驴妈妈自己开发的日历控件(该组件挺好可去),可后来产品经理又希望能修改年份月份,产品经理嘛,程序员都懂的,就不说了,所以才用了 jQueryUI组件,但jQueryUI对动态生成的元素不能直接使用,原因是在页面中创建了一个,然后通过对其显示隐藏来操作的,故对动态生成的要想办法对其每一个都创建一个与之对应的日历,有点类似对象下属性的意思,另外,对应已选择的日期要禁用掉,还好jQueryUI有类似的接口,万幸。。

附上驴妈妈日历效果,有兴趣的可以到网站上去取,前辈们写的很好,功能已经比较全面了。

calendar(); 
//定义一个封装函数,在每次创建时调用一下
function calendar(){ 
    $('.J_calendar').datepicker({
      changeMonth: true,
      changeYear: true,
      numberOfMonths: 2,
      beforeShowDay: nationalDays //禁用指定日期参数
    });
}

$('.icon_calendarLittle').live('click',function(){        
    $(this).prev().focus()
});

//禁用某天,每次操作重新更新数组,发给后台处理
natDays = [  [11,23,2013], [11,24,2014],[11,14,2014]  ];  
  
function nationalDays(date) {  
    for (i = 0; i < natDays.length; i++) {  
        if (date.getFullYear() == natDays[i][2] && date.getMonth() == natDays[i][0] - 1 && date.getDate() == natDays[i][1]) {  
            return [false, natDays[i][2] + '_day'];  
        }  
    }  
    return [true, ''];  
}  

####3、关于动态添加一些HTML结构或元素问题

underscorejs是一个比较不错的库,有研究过backbone的对此不会陌生,本人也只是知道些皮毛,对于项目中要动态生成一些相同结构的代码,可以使用其提供的方法。代码如下(改库依赖jquery,故别忘记引用):

//模板代码示例,注意 type,id
<script type="text/template" id="addTemplate">
    <div class="box" id="boxID_<%= idNum %>">
        <h5>第<%= idNum %>天</h5>
        <p><%- text %></p>
    </div>
</script>
//调用方式
var $div=$('<div></div>');
var json = {
    idNum : 1,
    text : "测试文字"
};
$div.html(_.template($('#addTemplate').html(), json );
//补充:<%=  %> 接受正常文本参数 <%-  %>可接受一些特殊字符文本

####4、上传照片页面与编辑页面的上传图片问题

这里专门研究了下plupload插件,这里不多说,有时间要单独写篇blog整理下。这个项目遇到主要的大坑,就是plupload的插件中的触发选择照片弹框的window窗口的元素的ID是固定的,项目中必须要不停的改变该ID,当时已经开发了一大半了,换插件是伤筋动骨啊,研究了一段时间发现原来是可以封装到方法了,通过传参来改变的,大喜。。

//插件的一些基本使用参数配置可在官方API中查找,此处指简单介绍部分
function myUpload(strID){
    var uploader = new plupload.Uploader({
        runtimes : 'html5,flash,silverlight,html4',
        browse_button : strID, 
        url : 'php/uploadImage.php',
        chunk_size : '10mb',
        //unique_names : true,
        filters : {
            max_file_size : '10mb',
            mime_types: [
                {title : "Image files", extensions : "jpg,gif,png"}
            ]
        },
        //resize : { width : 138, height : 91, quality : 90 },
        flash_swf_url : 'statics/js/Moxie.swf',
        silverlight_xap_url : 'statics/js/Moxie.xap',
    });
    uploader.init(); //初始化插件
    //FilesAdded方法用于将上传的元素添加到页面中,是个集合 files
    uploader.bind('FilesAdded',function(up,files){
        uploader.start();  //插件开始执行
        plupload.each(files, function(file) {                   
            //此处单个文件操作,如创建节点并插入的页面中,展示          
        });
    };
    //UploadProgress,上传进度
    uploader.bind('UploadProgress',function(up,file){
        //此处可进行上传过程的效果,如进度条
    });
    //FileUploaded,单个文件上传完成后执行
    uploader.bind('FileUploaded',function(up,file,res){
        //此处可发送Ajax,告诉服务器相关信息,并在Ajax成功时进行一些效果的制作
    });
    //Error,上传出错时执行
    uploader.bind('Error',function(up,file,res){
    })
}

####5、拖拽问题

该项目最变态的部分,简直是各种拖啊,说多了全是泪啊,拖拽原理不复杂,就不介绍了,主要是里面的关联与排序等。主要还是通过一个个if,将各个情况分开执行,这里一时半会也描述不清,慢慢写,慢慢调就好了。拖拽两个元素排序问题有个小技巧,就是判断该元素在其父元素的索引值进行比较,来进行是前面插入还是后面插入。另外,由于页面可能存在大量的可拖拽元素,必然能产生性能问题,所以,程序里尽量减少遍历查找等,通过定义变量来提高性能,还有尽量使用事件委托,通过e.target来进行操作等。但本人觉得其实最影响性能的是图片太大的问,所以,跟后台开发人员想了个办法就是再后台生成两套图片,一套是用户真正上传的原图,一套就是压缩的,只返回后台压缩的图片src对其进行拖拽操作,提高性能。 另外在拖拽图片进行排序时如何判断是向前排序还是向后排序,此处用了个简单的办法,就是每次将碰撞到的元素的索引值进行比较,来确定是向前还是向后。

上传过程中的图片预览效果目前只能用base64来处理,但会严重影响页面性能,最后将该功能取消了。

####6、左侧导航和描点定位

这个类似百度百科的效果,不同之处存在动态添加元素问题,没有找到相应的插件只好自己写,比较复杂,这里只介绍下原理和注意的地方:

  • 右侧滚动时左侧div的scrollTop要滚动多少? 由于存在动态插入问题,故要封装个函数,再每次插入后执行下重新计算高度,即getSetHeight,将父节点元素div应该scrollTop的值计算出来存在对应的data-height属性中,当window的scroll事件发生时,直接取data-height的值就好了。
  • 左侧div上的滚动监听事件 这里用到了一个插件mousewheel.js,使用比较简单,该插件很好的解决了两个问题:a、就是下方的小按钮的点亮与禁用效果。b、阻止了浏览器的一些默认事件,如:在该div上滚动,右侧的也随之滚动
  • 左侧锚点与右侧关联定位问题 我一开始是通过检测hash值的变换来做的,只需要让对应的元素的属性值(data-md=”m1”)与 hash值一一对应就好。在编辑页面时可以完成效果,可到了查看页,由于要实现对跳转链接进来的描点进行定位,并且顶部存在浮动的定位,这样只能用描点定位并让其产生偏移通过给a标签添加position:relative; top:-(偏移量)来实现。
//重新计算滚动高度 
function getSetHeight(){ 
    var H = 0; 
    $('.treeBox dl').each(function(i){ 
        H+=$('.treeBox dl').eq(i).outerHeight()+50; 
        $(this).attr('data-height', H) 
    }); 
}
//锚点定位
window.onhashchange = function(){    
    var hash = window.location.hash.substring(1);
    $('.writeTitle span').each(function(){
        if( $(this).attr('data-name') == hash ){               
            var T = $(this).offset().top;
            $('html,body').animate({
                scrollTop : T -140   //此处140是随便取的让填写部分在屏幕中上部
            },'fast')              
        }
    });
}
$('.treeBox dl dd a').live("click",function(ev){ 
    var id = $(this).parent().attr('id'); 
    $('.writeTitle span').each(function(){ 
        if($(this).attr('data-name')==id){ 
            var T = $(this).offset().top 
            $('html,body').animate({ 
                scrollTop : T-140 
            },'fast')
        } 
    }); 
    //return false; //同时屏蔽了hash值 
})

####7、查看页面的图片预览自适应效果

其实也很简单,从服务器那边把原始图片src取到,在图片onload后通过计算宽高比来进行适应屏幕的缩放就OK了。

    //加载图片尺寸计宽高
    var img = new Image();
    img.src = $("#"+picID).attr("to")||$("#"+picID).attr("src"); //附上路径
    var w =0;
    var h=0;
    var scale = 0;  //宽高比
    $('.picInfo i').html('D'+day.toUpperCase());
    $('.picInfo span').html(point);
    img.onload=function(){     //图片加载完进行相关计算
        scale = img.width/img.height;
        h = img.height > $(window).height()-200 ? $(window).height()-200 : img.height;
        if(!$("#"+picID).next().find('.t_articalTxt').length){
            h = h+100;
        }
        w = h*scale;
        if(w>$(window).width()-200){
            w = $(window).width()-200;
            h = w/scale;
        }
        var src = $(this).attr("src");
        $('.picIMG').width(w+42);
        $('.showLoadImg').attr('src',src).css({width:w,height:h});
    }