标签归档:JavaScript

ExtJS 拖拽模块的应用

因业务需要,只能动手来研究一个拖拽模块。研究之前觉得很简单,在我印象里好想类似的 Vue,jQuery 的插件非常多。应该是分分钟就可以搞定。结果花了很长的时间来研究这个玩意。现在把思路记下来方便下次查阅。

  • 创建拖拽对象
  • 确定“接受拖拽对象的”区域
  • 新建一个类,用于实现 ExtJS 中关于 “拖拽” 方法的实现
  • 使用新编写带有实现的类应用到拖拽对象中
  • 编写CSS来提升用户体验
  • 拖拽结束之后的节点复制和移动

基本上就是上述的几个步骤,为了方便查阅我把所有的脚本都构建在一个 JS 文件中,若真用到项目中,请严格按照 Sencha ExtJS 的 MVC 标准做代码的分离。代码如下:

/*
 * author:      liucan
 * email:       33370733@qq.com
 * description: 系统顶部模块
 */

// 在这里需要重写拖拽的方法,基础的 API 并没有基于业务的实现。
// 拖拽完成,必须要判定是否拖放在指定的区域中,并且长宽要一致
// 对其当前的容器元素,TOP,LEFT 必须为 0
var overrides = {
    startDrag: function () {
        this.originalXY = this.el.getXY();
    },
    onDrag: function () {
        this.el.addCls('item-draging');
    },
    onDragEnter: function (e, id) {
        // console.log('onDragEnter');
        if(id != this.el.dom.parentNode.id){
            this.el.addCls('item-drag-ok');
        }else{
            this.onDragOut();
        }
    },
    onDragOver: function (e, id) {
    },
    onDragOut: function (e, id) {
        this.el.removeCls('item-drag-ok');
    },
    onDragDrop: function (e, id) {
    },
    onInvalidDrop: function () {
        // 无效拖动,需要使用动画还原。
        this.invalidDrop = true;
        this.el.removeCls('item-draging');    
    },
    endDrag: function (e, id) {
        this.el.removeCls('item-draging');
        if(this.invalidDrop === true){
            // 无效的移动
            var animCfgObj = {
                easing: 'elasticOut',
                duration: 1,
                scope: this,
                callback: function(){
                    this.el.dom.style.postion = '';
                }
            }
            this.el.setXY(this.originalXY);
            delete this.invalidDrop;
        }else{
            // 判定 TARGET 的值,并且做好磁力贴效果
            // 复制节点到指定元素,删除原有节点
            var newNode = this.el;
            if(e.item){
                e.item.firstChild.append(newNode.dom)
                newNode.dom.style = "";
            }
        }
    }
};

var pinyinTpl = new Ext.XTemplate(
    '<div id="pinyin_panel" class="pinyin-panel">',
        '<tpl for=".">',
            '<div class="pinyin-item">{.}<div class="reset-this">x</div></div>',
        '</tpl>',
    '</div>'
);
var fontTpl = new Ext.XTemplate(
    '<div class="font-panel">',
        '<tpl for=".">',
            '<div class="thumb-wrap">',
                '<div class="thumb-pinyin"></div>',
                '<div class="thumb-font">{.}</div>',
            '</div>',
        '</tpl>',
        '<div style="clear: both"></div>',
    '</div>'
);

Ext.define('xjcms.view.util.Pinyin', {
    extend: 'Ext.window.Window',
    alias: 'widget.Pinyin',
    title: '生成拼音',
    requires: [
        'xjcms.view.util.pinyin.PinyinController',
        'xjcms.view.util.pinyin.PinyinModel'
    ],
    controller: {
        type: 'pinyin'
    },
    viewModel: 'pinyin',
    width: 600,
    minHeight: 320,
    layout: 'fit',
    scrollable: false,
    // 遮罩层
    modal: true,
    constrain: true,
    closable: true,
    autoDestroy: true,
    draggable: true,
    resizable: false,
    items: [{
        xtype: 'form',
        bodyPadding: '10 10 10 10',
        items: [{
            xtype: 'container',
            layout: 'hbox',
            items: [{
                xtype: 'textfield',
                fieldLabel: '文字',
                labelWidth: 60,
                width: 500,
                emptyText: '请输入文字',
                regex: /^([\u4e00-\u9fa5]+)/,
                regexText: '该输入框仅仅只支持输入汉字',
                value: ''
            }, {
                xtype: 'button',
                text: '生成拼音',
                handler: function () {
                    // word-lib/convert2Pinyin
                    var text = this.prev().getValue();
                    var vm = this.up('window').getViewModel();
                    var vmStores = vm.storeInfo;
                    var fontData = [];
                    text = Ext.util.Format.trim(text);
                    // 请求数据
                    Ext.Ajax.request({
                        url: 'https://easy-mock.com/mock/5ae02d829c4a407f53115d81/rms/py',
                        method: 'GET',
                        params: {
                            font: text
                        },
                        success: function (response, opts) {
                            var obj = Ext.decode(response.responseText);
                            // 中文字和拼音字母
                            var font = obj.data.font;
                            var py = obj.data.py;
                            vmStores['fontStore'] = Ext.create('Ext.data.Store', {
                                data: font
                            });
                            vmStores['pinyinStore'] = Ext.create('Ext.data.Store', {
                                data: py
                            });
                            vm.setStores(vmStores);
                        },
                        failure: function (response, opts) {
                            console.log('server-side failure with status code ' + response.status);
                        }
                    });
                }
            }]
        }, {
            xtype: 'dataview',
            tpl: pinyinTpl,
            itemSelector: 'div.pinyin-item',
            bind: {store: '{pinyinStore}'},
            listeners: {
                'refresh': function () {
                    var pinyins = Ext.select('.pinyin-item');
                    Ext.each(pinyins.elements, function (el) {
                        // 第一步,创建可拖动的元素
                        var dd = Ext.create('Ext.dd.DD', el, 'pyDDGroup', {
                            isTarget: false
                        });
                        Ext.apply(dd, overrides);
                    })
                }
            }
        }, {
            // 拼音的拖拽区域
            xtype: 'dataview',
            tpl: fontTpl,
            bind: {store: '{fontStore}'},
            itemSelector: 'div.thumb-wrap',
            emptyText: '<div class="font-none-data">暂时还没有任何文字哦</div>',
            listeners: {
                'refresh': function () {
                    var thumbWrap = Ext.select('.thumb-wrap');
                    // 第二步,创建可以用于接收的元素
                    Ext.each(thumbWrap.elements, function (el) {
                        var thumbWrapDDTarget = Ext.create('Ext.dd.DDTarget', el, 'pyDDGroup', {
                            ignoreSelf: false
                        });
                    })
                },
                'itemclick': function(thisViewObj, record, item, index, e, eOpts){
                    var clsName = e.target.className;
                    if(clsName === 'reset-this'){
                        // 移除这个元素
                        var pNode = e.target.parentNode;
                        // console.dir(pNode);
                        Ext.get('pinyin_panel').appendChild(pNode);
                    }
                }
            }
        }],
        buttons: [{
            xtype: 'button',
            text: '确定'
        }, {
            xtype: 'button',
            text: '取消',
            handler: function () {
                this.up('window').destroy();
            }
        }]
    }]
});

WebPack 傻瓜指南

知乎上的WebPack三连击,非常实用的东西。分享一下。

  • Webpack傻瓜式指南(一)http://zhuanlan.zhihu.com/FrontendMagazine/20367175
  • Webpack傻瓜式指南(二)http://zhuanlan.zhihu.com/FrontendMagazine/20397902
  • Webpack傻瓜式指南(三)http://zhuanlan.zhihu.com/FrontendMagazine/20522487

 

复习Javascript的一些代码(未完待续)

偶尔翻出来以前写的一些小东西。做应用开发久了,久而久之一开始就很注意框架的选择。但是最初写Javascript那种alert逐步调试。写一些通用的工具类的时间仿佛离我很远了。翻了下以前的一些代码,把他贴出来。就权当是复习下吧。

/**
 * author:      可乐加糖
 * description: 呵呵,不知道写什么好,随便吧。
 */

(function (window) {
    //基础类库
    var cola = {
        get_node: function (elem_id) {
            if (elem_id.constructor != String) {
                throw new Error("参数传递错误");
                return false;
            }
            return document.getElementById(elem_id) || null;
        },
        prev: function (elem) {
            do {
                elem = elem.previousElementSibling
            } while (elem && elem.nodeType != 1);
            return elem
        },
        next: function (elem) {
            do {
                elem = elem.nextSibling;
            } while (elem && elem.nodeType != 1);
            return elem
        },
        first_node: function (elem) {
            elem = elem.firstChild;
            return elem && elem.nodeType != 1 ? this.next(elem) : elem;
        },
        last: function (elem) {
            elem = elem.lastChild;
            return elem && elem.nodeType != 1 ? prev(elem) : elem;
        },
        parent: function (elem, num) {
            num = num || 1;
            for (var i = 0; i < num; i++) {
                if (elem != null)
                    elem = elem.parentNode;
                return elem;
            }
        },
        remove: function (elem) {
          if(elem)
            elem.parentNode.removeChild(elem);
        },
        empty: function (elem) {
            while(elem.firstChild)
                elem.removeChild(elem.firstChild)
        }
    }
    window.cola = cola;
})(window)

这是最开始写的一个二级下拉导航条的代码

document.getElementsByClassName = function(clsName){
    if(typeof(clsName)!="string" || clsName.constructor != String){
        throw new Error("className必须是字符串");
        return false;
    }
    var nodes = document.getElementsByTagName("*");
    var nodeArr = new Array();
    for(var i= 0,icount = nodes.length;i<icount;i++){
        if(nodes[i].className == clsName)
            nodeArr.push(nodes[i]);
    }
    return nodeArr;
}
window.onload = function(){
    var nodes = document.getElementsByClassName("has-child");
    for(var i= 0,icount = nodes.length;i<icount;i++){
        nodes[i].onmouseover = function(){
            var child_menu = this.getElementsByTagName("ul");
            child_menu[0].style.display = "block";
        }
        nodes[i].onmouseout = function () {
            var child_menu = this.getElementsByTagName("ul");
            child_menu[0].style.display = "none";
        }
    }
}

还有获取元素的样式表,当初也是在IE下调试了很久才搞定。

function getStyle(elem,name){
    if(elem.style[name])
        return elem.style[name];
    else if(elem.currentStyle)
        return elem.currentStyle[name];
    else if(document.defaultView && document.defaultView.getComputedStyle){
        name = name.replace(/([A-Z])/g,"-$1");
        name = name.toLowerCase();
        var s = document.defaultView.getComputedStyle(elem,"");
        return s && s.getPropertyValue(name);
    }
    else
        return null;
}

Javascript属性constructor/prototype的底层原理(转)

最近一直在阅读 JQuery 的源码,涉及到核心组件,需要复习一下 JavaScript 的底层属性,把这篇文章转载过来,作为资料。
原帖地址:http://blog.csdn.net/hikvision_java_gyh/article/details/8937157

在Javascript语言中,constructor属性是专门为function而设计的,它存在于每一个function的prototype属性中。这个constructor保存了指向function的一个引用。在定义一个函数(代码如下所示)时,


function F() {   
    // some code  
 }

JavaScript内部会执行如下几个动作:

为该函数添加一个原形属性(即prototype对象).

为prototype对象额外添加一个constructor属性,并且该属性保存指向函数F的一个引用。

这样当我们把函数F作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数F)的prototype对象的一个属性__proto__,所以我们在每一个对象实例中就可以访问构造函数的prototype所有拥有的全部属性和方法,就好像它们是实例自己的一样。

当然该实例也有一个constructor属性了(从prototype那里获得的),这时候constructor的作用就很明显了,因为在这时,每一个对象实例都可以通过constrcutor对象访问它的构造函数,请看下面代码:

 

[javascript]?view plaincopy


var?f?=?new?F();
alert(f.constructor?===?F);//?output?true
alert(f.constructor?===?F.prototype.constructor);//?output?true

我们可以利用这个特性来完成下面的事情: 对象类型判断,如

[javascript]?view plaincopy


if(f.constructor?===?F)?{
    //?do?sth?with?F
}

其实constructor的出现原本就是用来进行对象类型判断的,但是constructor属性易变,不可信赖。
我们有一种更加安全可靠的判定方法:instanceof 操作符。
下面代码仍然返回true


if(f instanceof F)?{
    // do sth with F
}.

原型链继承,由于constructor存在于prototype对象上,因此我们可以结合constructor沿着原型链找到最原始的构造函数,如下面代码:


function Base() {
}
// Sub1 inherited from Base through prototype chain
function Sub1() {
}
Sub1.prototype = new Base();
Sub1.prototype.constructor = Sub1;
Sub1.superclass = Base.prototype;
// Sub2 inherited from Sub1 through prototype chain
function Sub2() {
}
Sub2.prototype = new Sub1();
Sub2.prototype.constructor = Sub2;
Sub2.superclass = Sub1.prototype;
// Test prototype chain
alert(Sub2.prototype.constructor);
// function Sub2(){}
alert(Sub2.superclass.constructor);
// function Sub1(){}
alert(Sub2.superclass.constructor.superclass.constructor);
// function Base(){}

上面的例子只是为了说明constructor在原型链中的作用,更实际一点的意义在于:一个子类对象可以获得其父类的所有属性和方法,称之为继承,关于继承我们有好多可以分析和讨论,本篇限于篇幅不在此讨论。
一个容易掉入的陷阱(gotchas) 之前提到constructor易变,那是因为函数的prototype属性容易被更改,我们用时下很流行的编码方式来说明问题,请看下面的示例代码:


function F() {

}
F.prototype = {
    _name:Eric,
    getName: function () {
        return this._name;
    }
}

初看这种方式并无问题,但是你会发现下面的代码失效了:

var f = new F();
alert(f.constructor === F); // output false

怎么回事?F不是实例对象f的构造函数了吗?
当然是!只不过构造函数F的原型被开发者重写了,这种方式将原有的prototype对象用一个对象的字面量{}来代替。
而新建的对象{}只是Object的一个实例,系统(或者说浏览器)在解析的时候并不会在{}上自动添加一个constructor属性,因为这是function创建时的专属操作,仅当你声明函数的时候解析器才会做此动作。
然而你会发现constructor并不是不存在的,下面代码可以测试它的存在性:

alert(typeof f.constructor == ‘undefined’);// output false

既然存在,那这个constructor是从哪儿冒出来的呢?

我们要回头分析这个对象字面量{}。

因为{}是创建对象的一种简写,所以{}相当于是new Object()。

那既然{}是Object的实例,自然而然他获得一个指向构造函数Object()的prototype属性的一个引用__proto__,又因为Object.prototype上有一个指向Object本身的constructor属性。所以可以看出这个constructor其实就是Object.prototype的constructor,下面代码可以验证其结论:

alert(f.constructor === Object.prototype.constructor);//output true
alert(f.constructor === Object);// also output true

一个解决办法就是手动恢复他的constructor,下面代码非常好地解决了这个问题:


function F() {
}
F.prototype = {
    constructor: F, /* reset constructor */
    _name: Eric,
    getName: function () {
        return this._name;
    }
};

之后一切恢复正常,constructor重新获得的构造函数的引用,我们可以再一次测试上面的代码,这次返回true
var f = new F();
alert(f.constructor === F); // output true this time ^^
解惑:构造函数上怎么还有一个constructor?它又是哪儿来的?
细心的朋友会发现,像JavaScript内建的构造函数,如Array, RegExp, String, Number, Object, Function等等居然自己也有一个constructor:
alert(typeof Array.constructor != ‘undefined’);// output true
经过测试发现,此物非彼物它和prototype上constructor不是同一个对象,他们是共存的:
alert(typeof Array.constructor != ‘undefined’);// output true
alert(typeof Array.prototype.constructor === Array); // output true
不过这件事情也是好理解的,因为构造函数也是函数。
是函数说明它就是Function构造函数的实例对象,自然他内部也有一个指向Function.prototype的内部引用__proto__啦。
因此我们很容易得出结论,这个constructor(构造函数上的constructor不是prototype上的)其实就是Function构造函数的引用:
alert(Array.constructor === Function);// output true
alert(Function.constructor === Function); // output true
OK, constructor从此真相大白,你不在对它陌生了~

Sencha Extjs4.2 皮肤制作

话不多说,直接上图:

1

搜了一把皮肤制作,大多数都是英文文档,看了几遍差不多熟练了之后就把皮肤问题得以搞定,记录下来方便下次使用。

步骤:

1.?安装环境,JDK、ruby、compass、sass (JDK 通过下载安装,RUBY 通过下载安装- http://rubyinstaller.og ,sass 通过Ruby 中的 gem 能够自动安装)

2.?下载 SenchaCmd-3.1.2.342-windows.exe 、SenchaSDKTools-2.0.0-beta3-windows.exe (Linux 环境请自行下载安装包,配置PATH)

3. 环境搭建完毕之后,开始构建工程,使用命令
sencha –sdk /extjs的路径/ generate app 工程名 工程存放的路径
2

3

继续阅读

Html5 属性浏览器速查

网址:http://caniuse.com/#index

Html5属性支持

Html5属性支持

该网站提供了非常丰富的H5以及各种新型浏览器API,并且能够在网站上快速搜索某一属性在各个浏览器以及设备上的支持情况。
例如:搜索GEOLOCATION属性的支持。

Html5属性支持

Html5属性支持

这样极大的方便了日常H5开发的浏览器支持一览,如果需要支持低端浏览器,可以在重构HTML之前就查询相应的支持,从而判断如何使用降级版本处理方案。

ExtJs 自定义组件事件绑定

优化了一下代码,结果如下。

调用:

<script type="text/javascript">
function aaa(){
	var student_panel = new WX.student.AddStudent({
		callback_fun:function(){
			alert("我是页面的方法");
		}
	}).show();
}
</script>

组件定义

Ext.define('WX.student.AddStudent', {
	
    extend: 'Ext.window.Window',

    modal:true,
    height: 585,
    width: 590,
    layout: {
        type: 'fit'
    },
    title: '新增学生',
    //用于组件函数回调
    callback_fun:function(){},
    initComponent: function() {
    	
        var me = this;
        
        Ext.applyIf(me, {
            items: [
                {
                    waitTitle: '加载中...',
                    items: [
                        ......
                    ],
                    buttons:[{
                    	text:'保存',
                    	scope:this,                  // <============== 关键参数 
                    	handler:function(){
                    		//DO SOMETHING...
                    		alert("组件的事件");
                    		this.callback_fun();
                    	}
                    }]
                }
            ]
        });
        me.callParent(arguments);
    }

});

Sencha 自定义组件函数回调

工程里用到了Sencha ExtJs,因为特定的组件原因,导致很多组件得自己。组件定义完成之后的 Success_callback 因为之前没做过,所以这次把它记录下来方便以后查阅。直接上代码:

界面调用

<script type="text/javascript">
function aaa(){
	var student_panel = new WX.student.AddStudent({
		//传入回调函数,直接显示。
		callback_fun:function(){
			alert('我是界面的回调函数');
		}
	}).show();
}
</script>

组件部分代码

Ext.define('WX.student.AddStudent', {

    extend: 'Ext.window.Window',

    modal:true,
    height: 585,
    width: 684,
    layout: {
        type: 'fit'
    },
    title: '新增学生',
    bodyPadding:'10 10 10 10',
    //用于组件函数回调
    callback_fun:function(){},
    initComponent: function() {
        var me = this;
        ......
        // Panel 组件
        buttons:[{
                text:'保存',
                handler:function(){
                     //发送Ajax请求保存用户数据.

                    //调用STORE之后的自定义回调函数
                    this.callback_fun
                }
        }]
    }
.....

这里的做法有点类似Java的接口回调,在自定义模块中定义方法,提供按钮回调,界面初始化组件的时候用方法覆盖组件中的回调方法,达到回调的目的。

tabs复习(jQuery)

已经不记得是多久没有写过了,最近的状态还不错,因为各种考试的缘故,很多技术文章都是看了,没有往上面写。在亚马逊购了一些H5,CSS3以及移动互联网开发的书,等驾照考过之后就又要开启学习模式了。

今天闲来无聊,也就把jQuery里面的几个点复习了一边,写了一个小DEMO。复习点是jQuery的自定义事件。

直接帖代码上来了。

jQuery.fn.tabs = function (control) {
    var element = $(this);
    control = $(control);
    //初始化完毕

    element.delegate("li", "click", function () {
        //找到需要修改的目标
        var tabName = $(this).attr("data-tab");
        //点击选项卡触发自定义事件
        element.trigger("change.tabs", tabName);
    });

    element.bind("change.tabs", function (e, tabname) {
        element.find("li").removeClass("active");
        element.find(">[data-tab='" + tabname + "']").addClass("active");
    });

    element.bind("change.tabs", function (e, tabname) {
        control.find(">[data-tab]").removeClass("active");
        control.find(">[data-tab='" + tabname + "']").addClass("active");
    });

    var firstName = element.find("li:first").attr("data-tab");
    element.trigger("change.tabs",firstName);

    return this;
}

自定义事件在日常的WEB开发中是非常常见的功能,用于封装自有组件定义自有API。实在是居家旅行,开发面试必备技能。

JavaScript错误与调试

前端开发中,应该除非大型团队对编程有着严格的日志控制和错误机制,在小型团队中的前端使用错误与调试还是基本上靠Firebug等调试工具。前端迭代时间短周期也比较短,在这方面也的确是一个欠缺。

阅读了《编写可维护的JavaScript》书中的第十章 ”抛出自定义错误“ 对代码的书写和规范还是有一定的启发。把一些有用的东西作为笔记记下来。

错误类型:

  • Error – 基本错误类型
  • EvalError – 通过 eval() 函数执行代码发生的错误抛出
  • RangeError – 数字超出其边界时抛出(例:创建一个负数索引的数组)
  • ReferenceError – 期望的对象不存在
  • SyntaxError – 给 eval() 函数传递的代码中有语法错误
  • TypeError – 变量不是期望的类型(new 10,’prop’ in true)
  • URLError – 常见于 encodeURI()、encodeURIComponent(),decodeURI() 等传递了非法格式的URI字符串时抛出。

关于 Try-Catch 的应用

  • 修复了一个难以调试和修复的BUG之后,尝试增加自定义错误,以便于下次调试时快速的定位错误代码。
  • 编写代码时的一些防御型、预见性的错误可以尝试添加错误处理函数。
  • 正在编写的代码需要提供给其他人使用,如果按照对方的思维,在特定的情况中抛出错误处理

错误以及错误处理不是为了防止错误,而是为了在错误发生时能够更加快速的调试和定位错误。