[ Content | Sidebar ]

Archives for web dev

相对完美的设计思路~

有没有可能写一套代码适应以下三种情况:
1. 客户端没有开启javascript,或者使用ucweb一类的弱智浏览器访问时,功能没有影响
2. 客户端开启了javascript,每个页面都有单独加载的js,但是禁用ajax,只帮助用户交互
3. 客户端开启了javascript,为了提高载入速度,尽量使所有操作用ajax完成

略有所得: http://www.blogo2.com
源代码: http://code-of-emptyhua.googlecode.com/svn/trunk/blogo2

没有禁用js的时候,页面切换,表单提交(注册和登录没有用)尽量的使用ajax,而且部分操作会使用弹层(登录后的管理),禁用js之后,则所有操作换成普通表单提交在页面之间跳来跳去。。

还有一个功能没有开启,就是可以选择不用ajax,但是独立开的页面js依然有效。

有兴趣的同学可以check源代码,这种模式的实现:
1. 开发的时候禁用ajax,单个页面开发,而且要求禁用js时业务可以走通,页面内的js和css统一由模板引入。
2. 对需要开启ajax的链接增加hook,例如data-ajax=”dialog:{}”,则表明在开启ajax模式后,点击这个链接需要弹窗
3. ajax模式下只需要对单个页面更换特有的模板,动态加载需要的js和css即可。

这种设计的特点:
1. 尽量一个人包前后端。。
2. json不潮了,ajax返回html代码,优点是js不用套页面了,缺点是流量大

over,虽然没有完美的代码,但是我尽量~

———–
post by gmail~

ie-css3.htc的修正增强版

做前端的同学都应该听说或者用过,是一段脚本,可以让ie实现css3里的圆角和阴影效果:http://fetchak.com/ie-css3/

用法大致如下

.box {
  -moz-border-radius: 15px; /* Firefox */
  -webkit-border-radius: 15px; /* Safari and Chrome */
  border-radius: 15px; /* Opera 10.5+, future browsers, and now also Internet Explorer 6+ using IE-CSS3 */
 
  -moz-box-shadow: 10px 10px 20px #000; /* Firefox */
  -webkit-box-shadow: 10px 10px 20px #000; /* Safari and Chrome */
  box-shadow: 10px 10px 20px #000; /* Opera 10.5+, future browsers and IE6+ using IE-CSS3 */
 
  behavior: url(ie-css3.htc); /* This lets IE know to call the script on all elements which get the 'box' class */
}
Copy Code 

最近用到了这个东西,发现动态改变div的内容之后,这段脚本生成的vml会出现变形。。
所以加了一个手动刷新的函数,通过innerHTML赋值之后调用一下就可以了

el.innerHTML = '....';
if(window.update_css3_fix) update_css3_fix(el);
Copy Code 

如果使用jquery就不用这么麻烦,在你的框架里加一段

(function()
{
    if (!jQuery.browser.msie) return;
    jQuery.fn.__ohtml__ = jQuery.fn.html;
    jQuery.fn.html = function(value)
    {
        jQuery(this).__ohtml__(value);
        this.each(function()
        {
            update_css3_fix(this);
        });
        return this;
    };
})();
Copy Code 

另外官网下载的脚本还会产生yourdomain/none的404请求,也已经修复

修改之后的文件ie-css3.htc

js中简单实现数据和ui绑定

很简单的例子

//计数加一
count ++
//更新ui
jQuery('#count').text(count);
Copy Code 

相信几乎所有前端工程师都会写过上面这样的代码,更新数据,然后更新ui,行云流水般的。 然而也是相当一部分内测bug的起源,数据删除了,ui里的计数没有变,或者ui变了,数据里确没有删除。而在逻辑复杂的应用里,更是很难保证不出这种低级错误。

其实我们可以通过自定义事件,这样重写

this.data_bind('count', 'change', function(count)
{
    //更新计数
    jQuery('#count').text(count);
});
....
 
this.data_bind('count', 'change', function(count)
{
    //刷新分页
    refresh_pager(count);
});
 
this.set_data('count', this.get_data('count') + 1);
Copy Code 

上次D2上听金大为讲模板技术,当时感觉耳目一新,最近边写边考虑这些新思想,其实使用现有的自定义事件要简单的多,而且灵活性,适用性更好。

用过flex的同学看完这篇文章肯定会说:靠,你们写js的还真是原始社会。。

js业务框架中对ajax的再次包装

一个简单的load动作,如果简单用任何一个js框架实现

startloding();
new ajax({
    url : 'test.php',
    compete : function()
    {
         stoploading()
    },
    success : function(data)
    {
        try
        {
            var obj = json_decode(data);
        }
        catch(e)
        {
            //服务异常,未返回json造成异常
            error('服务器异常');
            return;
        }
 
        //正常的业务逻辑错误
        if ( json['code'] !== 0 )
        {
            error(json['desc']);
            return;
        }
 
        ...
    },
    error : function()
    {
        //网络请求失败造成异常
        error('网络异常');
    }
});
Copy Code 

而一个业务框架对ajax再次包装,成为一个action

new action({
    action : 'load',
    action_name : '加载',
    url : 'test.php'
});
 
api.add_action('load_start', startloading);
api.add_action('load_complete', stoploading);
//这里的成功不是ajax成功,而是判定json['code']之后action真正的成功,这就要求所有action需要有统一的成功判定标准,和json包装格式
api.add_action('load_success', function(msg, data){  .... });
//这里没有添加异常处理,因为没有特殊需要,所以action会执行默认的错误处理,可能是一个弹窗
Copy Code 

不仅仅是节省了代码,后者有更好的可读性,load动作本身也具有了更好的重用性

具体的框架代码已经在用,还不是很完美,待贴~

php的一个表单验证类

做web这么长时间,感觉最恶心的就是表单验证这块了,很无聊,很烦琐,也很重要..
这个验证类的功能参考了codeigniter的表单验证库的功能,按照我的想法实现完毕又去对照codeigniter的代码,感觉还是我的这个比较灵活实用

/**
 * 表单验证类
 * Author : emptyhua@gmail.com
 * Version : beta
 */
 
class FormValidator
{
    //所有验证函数定义时的前缀
    const PREFIX = 'form_validator_';
 
    //存储验证函数的出错信息
    public static $error_messages = array();
 
    //存储验证相关的数据
    protected $valid_rules = array(); 
 
    //存储上次验证的出错信息
    protected $errors = array();
 
    //是否完全检查
    protected $complete_check = true;
 
    public static function set_error($rule, $error)
    {
        self::$error_messages[$rule] = $error;
    }
 
    /**
     * 遇到第一个错误便返回
     */
 
    public function first_error_only()
    {
        $this->complete_check = false; 
    }
 
    /**
     * 执行所有验证规则,收集所有错误
     */
 
    public function show_all_errors()
    {
        $this->complete_check = true; 
    }
 
    /**
     * 设置验证规则
     * @param string $var 验证的变量名,例如 username
     * @param string $var_name 变量名的描述,例如 用户名
     * @param string $func_name 验证规则,可以是多个,并且可以传参,例如 require|length(9,12)
     * @param string $error_message 自定义的出错信息,可选,如果给定将覆盖验证函数的默认出错信息
     */
 
    public function set_rules($var, $var_name, $func_name, $error_message = NULL)
    {
        $data = array();
        $data['var_name'] = $var_name;
        $data['error'] = $error_message;
        $data['funcs'] = array();
        $funcs_with_arg = explode('|', $func_name);
        $args_match = array();
 
        //解析验证规则
        foreach( $funcs_with_arg as $func_with_arg )
        {
            preg_match('/\((.*)\)$/', $func_with_arg, $args_match);
            //规则函数是否携带参数
            if ( count($args_match) > 0 )
            {
                //参数列表
                $args = explode(',', $args_match[1]);
                //去掉参数后的验证规则名
                $func = str_replace('(' . $args_match[1] . ')', '', $func_with_arg);
                $data['funcs'][$func] = $args;
            }
            else
            {
                $data['funcs'][$func_with_arg] = NULL;
            }
        }
 
        $this->valid_rules[$var] = $data;
    }
 
    /**
     * 检验一组数据是否合法
     * @param array $data 一个关联数组
     * @return boolean
     */
 
    public function is_valid($data)
    {
        $this->errors = array();
        $valid = true;
        //遍历的顺序为规则添加的顺序
        foreach( $this->valid_rules as $var => $rule )
        {
            //判断数据中是否有此字段,没有的话置一个空的
            if (!isset($data[$var])) 
                $data[$var] = '';
 
            foreach($rule['funcs'] as $func => $args)
            {
                //真实的验证函数的名称,例如 form_validator_require
                $func_run = self::PREFIX . $func;
                //如果函数未定义,循环继续
                if ( !is_callable($func_run) ) continue;
 
                //为验证函数组装参数
                //顺序为:当前验证字段的值, 所有验证数据, 验证规则参数1, ...
                if ( $args === NULL ) 
                {
                    $args = array($data[$var], $data);
                }
                else
                {
                    array_unshift($args, $data);
                    array_unshift($args, $data[$var]);
                }
 
                //调用验证函数
                if ( !call_user_func_array($func_run, $args) )
                {
                    $valid = false;
                    //如果set_rules的时候没有自定义错误信息,则使用默认出错信息
                    if ( $rule['error'] === NULL )
                    {
                        //使用sprintf拼接错误信息
                        //参数顺序: 验证字段的描述, 验证函数的参数1, 验证函数的参数2 ...
 
                        //去掉刚才添加的字段值
                        array_shift($args);
                        //去掉刚才添加的所有验证数据
                        array_shift($args);
                        //添加字段名的描述
                        array_unshift($args, $rule['var_name']);
                        //添加默认的出错信息
                        array_unshift($args, self::$error_messages[$func]);
                        $this->errors[] = call_user_func_array('sprintf', $args);
                    }
                    else
                    {
                        //使用自定义错误信息
                        $this->errors[] = $rule['error'];
                        continue 2;
                    }
 
                    //如果设置不进行完全检查,则发现第一个错误后跳出最外层循环
                    if ( !$this->complete_check )
                    {
                        break 2; 
                    }
                }
            }
        }
 
        return $valid;
    }
 
    /**
     * 获取出错信息
     * @return string array
     */
 
    public function get_errors()
    {
        return $this->errors; 
    }
 
    /**
     * 返回第一条出错信息
     * @return string
     */
 
    public function get_first_error()
    {
        return $this->errors[0];
    }
 
}
 
//扩展验证规则很方便~
//设置规则的默认出错信息
FormValidator::set_error('require', '%s不能为空');
 
//定义验证函数,以FormValidator::PREFIX打头
function form_validator_require($str)
{
    return !(trim($str) == '');
}
 
FormValidator::set_error('equal', '重复输入不一致');
 
function form_validator_equal($str, $data, $eqto)
{
    if ( !isset($data[$eqto]) )
    {
        return false;
    }
 
    return $data[$eqto] == $str;
}
Copy Code 

使用方法

$vd = new FormValidator();
$vd->set_rules('p', '密码',  'require|length(9,12)');
$vd->set_rules('p2', '密码验证', 'equa(p)', '两次密码输入不一致');
if (!$vd->is_valid($_POST))
{
    var_dump($vd->get_errors());
}
Copy Code