`
雷诺阿
  • 浏览: 17624 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

javascript组件开发(转自CSDN bingqingsuimeng的专栏)

阅读更多
<script charset="utf-8" type="text/javascript" src="http://c.csdnimg.cn/pubfooter/js/tracking.js"></script><script type="text/javascript">// <![CDATA[ var protocol = window.location.protocol; document.write('<script type="text/javascript" src="' + protocol + '//csdnimg.cn/pubfooter/js/repoAddr2.js?v=' + Math.random() + '"></' + 'script>'); // ]]></script><script id="allmobilize" charset="utf-8" type="text/javascript" src="http://a.yunshipei.com/46aae4d1e2371e4aa769798941cef698/allmobilize.min.js"></script>

javascript组件开发方式

<script type="text/javascript" src="http://static.blog.csdn.net/scripts/jquery.js"></script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/ad.js?v=1.1"></script><!--new top-->

 

<!--new top--><!--new top--> <script id="toolbar-tpl-scriptId" type="text/javascript" src="http://static.csdn.net/public/common/toolbar/js/html.js"></script><!--new top-->
 
<script type="text/javascript">// <![CDATA[ var username = "bingqingsuimeng"; var _blogger = username; var blog_address = "http://blog.csdn.net/bingqingsuimeng"; var static_host = "http://static.blog.csdn.net"; var currentUserName = ""; // ]]></script>
 
<script type="text/javascript" language="JavaScript" src="http://download.csdn.net/js/jquery.cookie.js"></script><script type="text/javascript" src="http://c.csdnimg.cn/rabbit/search-service/main.js"></script><script type="text/javascript" src="http://static.blog.csdn.net/public/res/bower-libs/MathJax/MathJax.js?config=TeX-AMS_HTML"></script><script type="text/ecmascript">// <![CDATA[ window.quickReplyflag = true; // ]]></script>

javascript组件开发方式

 

作为一名前端工程师,写组件的能力至关重要。虽然javascript经常被人嘲笑是个小玩具,但是在一代代大牛的前仆后继的努力下,渐渐的也摸索了一套组件的编写方式。

下面我们来谈谈,在现有的知识体系下,如何很好的写组件。

比如我们要实现这样一个组件,就是一个输入框里面字数的计数。这个应该是个很简单的需求。

图片

我们来看看,下面的各种写法。

为了更清楚的演示,下面全部使用jQuery作为基础语言库。

最简陋的写法

嗯 所谓的入门级写法呢,就是完完全全的全局函数全局变量的写法。(就我所知,现在好多外包还是这种写法)

代码如下:


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>test</title>
  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  <script>
    $(function() {

      var input = $('#J_input');

      //用来获取字数
      function getNum(){
        return input.val().length;
      }

      //渲染元素
      function render(){
        var num = getNum();

        //没有字数的容器就新建一个
        if ($('#J_input_count').length == 0) {
          input.after('<span id="J_input_count"></span>');
        };

        $('#J_input_count').html(num+'个字');
      }

      //监听事件
      input.on('keyup',function(){
        render();
      });

      //初始化,第一次渲染
      render();


    })
  </script>
</head>
<body>
<input type="text" id="J_input"/>
</body>
</html>

这段代码跑也是可以跑的,但是呢,各种变量混乱,没有很好的隔离作用域,当页面变的复杂的时候,会很难去维护。目前这种代码基本是用不了的。当然少数的活动页面可以简单用用。

作用域隔离

让我们对上面的代码作些改动,使用单个变量模拟命名空间。

var textCount = {
  input:null,
  init:function(config){
    this.input = $(config.id);
    this.bind();
    //这边范围对应的对象,可以实现链式调用
    return this;
  },
  bind:function(){
    var self = this;
    this.input.on('keyup',function(){
      self.render();
    });
  },
  getNum:function(){
    return this.input.val().length;
  },
  //渲染元素
  render:function(){
    var num = this.getNum();

    if ($('#J_input_count').length == 0) {
      this.input.after('<span id="J_input_count"></span>');
    };

    $('#J_input_count').html(num+'个字');
  }
}

$(function() {
  //在domready后调用
  textCount.init({id:'#J_input'}).render();
})

这样一改造,立马变的清晰了很多,所有的功能都在一个变量下面。代码更清晰,并且有统一的入口调用方法。

但是还是有些瑕疵,这种写法没有私有的概念,比如上面的getNum,bind应该都是私有的方法。但是其他代码可以很随意的改动这些。当代码量特别特别多的时候,很容易出现变量重复,或被修改的问题。

于是又出现了一种函数闭包的写法:

var TextCount = (function(){
  //私有方法,外面将访问不到
  var _bind = function(that){
    that.input.on('keyup',function(){
      that.render();
    });
  }

  var _getNum = function(that){
    return that.input.val().length;
  }

  var TextCountFun = function(config){

  }

  TextCountFun.prototype.init = function(config) {
    this.input = $(config.id);
    _bind(this);

    return this;
  };

  TextCountFun.prototype.render = function() {
    var num = _getNum(this);

    if ($('#J_input_count').length == 0) {
      this.input.after('<span id="J_input_count"></span>');
    };

    $('#J_input_count').html(num+'个字');
  };
  //返回构造函数
  return TextCountFun;

})();

$(function() {
  new TextCount().init({id:'#J_input'}).render();
})

这种写法,把所有的东西都包在了一个自动执行的闭包里面,所以不会受到外面的影响,并且只对外公开了TextCountFun构造函数,生成的对象只能访问到init,render方法。这种写法已经满足绝大多数的需求了。事实上大部分的jQuery插件都是这种写法。

面向对象

上面的写法已经可以满足绝大多数需求了。

但是呢,当一个页面特别复杂,当我们需要的组件越来越多,当我们需要做一套组件。仅仅用这个就不行了。首先的问题就是,这种写法太灵活了,写单个组件还可以。如果我们需要做一套风格相近的组件,而且是多个人同时在写。那真的是噩梦。

在编程的圈子里,面向对象一直是被认为最佳的编写代码方式。比如java,就是因为把面向对象发挥到了极致,所以多个人写出来的代码都很接近,维护也很方便。但是很不幸的是,javascript不支持class类的定义。但是我们可以模拟。

下面我们先实现个简单的javascript类:

var Class = (function() {
  var _mix = function(r, s) {
    for (var p in s) {
      if (s.hasOwnProperty(p)) {
        r[p] = s[p]
      }
    }
  }

  var _extend = function() {

    //开关 用来使生成原型时,不调用真正的构成流程init
    this.initPrototype = true
    var prototype = new this()
    this.initPrototype = false

    var items = arguments.slice() || []
    var item

    //支持混入多个属性,并且支持{}也支持 Function
    while (item = items.shift()) {
      _mix(prototype, item.prototype || item)
    }


    // 这边是返回的类,其实就是我们返回的子类
    function SubClass() {
      if (!SubClass.initPrototype && this.init)
        this.init.apply(this, arguments)//调用init真正的构造函数
    }

    // 赋值原型链,完成继承
    SubClass.prototype = prototype

    // 改变constructor引用
    SubClass.prototype.constructor = SubClass

    // 为子类也添加extend方法
    SubClass.extend = _extend

    return SubClass
  }
  //超级父类
  var Class = function() {}
  //为超级父类添加extend方法
  Class.extend = _extend

})()

这是拿John Resig的class简单修改了下。

这边只是很简陋的实现了类的继承机制。如果对类的实现有兴趣可以参考我另一篇文章javascript oo实现

我们看下使用方法:

//继承超级父类,生成个子类Animal,并且混入一些方法。这些方法会到Animal的原型上。
//另外这边不仅支持混入{},还支持混入Function
var Animal = Class.extend({
  init:function(opts){
    this.msg = opts.msg
    this.type = "animal"
  },
  say:function(){
    alert(this.msg+":i am a "+this.type)
  }
})


//继承Animal,并且混入一些方法
var Dog = Animal.extend({
  init:function(opts){
    //并未实现super方法,直接简单使用父类原型调用即可
    Animal.prototype.init.call(this,opts)
    //修改了type类型
    this.type = "dog"
  }
})

//new Animal({msg:'hello'}).say()

new Dog({msg:'hi'}).say()

使用很简单,超级父类具有extend方法,可以继承出一个子类。子类也具有extend方法。

这边要强调的是,继承的父类都是一个也就是单继承。但是可以通过extend实现多重混入。详见下面用法。

有了这个类的扩展,我们可以这么编写代码了:


var TextCount = Class.extend({
  init:function(config){
    this.input = $(config.id);
    this._bind();
    this.render();
  },
  render:function() {
    var num = this._getNum();

    if ($('#J_input_count').length == 0) {
      this.input.after('<span id="J_input_count"></span>');
    };

    $('#J_input_count').html(num+'个字');

  },
  _getNum:function(){
    return this.input.val().length;
  },
  _bind:function(){
    var self = this;
    self.input.on('keyup',function(){
      self.render();
    });
  }
})

$(function() {
  new TextCount({
    id:"#J_input"
  });
})

这边可能还没看见class的真正好处,不急我们继续往下。

抽象出base

可以看到,我们的组件有些方法,是大部分组件都会有的。

  • 比如init用来初始化属性。
  • 比如render用来处理渲染的逻辑。
  • 比如bind用来处理事件的绑定。

当然这也是一种约定俗成的规范了。如果大家全部按照这种风格来写代码,开发大规模组件库就变得更加规范,相互之间配合也更容易。

这个时候面向对象的好处就来了,我们抽象出一个Base类。其他组件编写时都继承它。

var Base = Class.extend({
  init:function(config){
    //自动保存配置项
    this.__config = config
    this.bind()
    this.render()
  },
  //可以使用get来获取配置项
  get:function(key){
    return this.__config[key]
  },
  //可以使用set来设置配置项
  set:function(key,value){
    this.__config[key] = value
  },
  bind:function(){
  },
  render:function() {

  },
  //定义销毁的方法,一些收尾工作都应该在这里
  destroy:function(){

  }
})

base类主要把组件的一般性内容都提取了出来,这样我们编写组件时可以直接继承base类,覆盖里面的bind和render方法。

于是我们可以这么写代码:

var TextCount = Base.extend({
  _getNum:function(){
    return this.get('input').val().length;
  },
  bind:function(){
    var self = this;
    self.get('input').on('keyup',function(){
      self.render();
    });
  },
  render:function() {
    var num = this._getNum();

    if ($('#J_input_count').length == 0) {
      this.get('input').after('<span id="J_input_count"></span>');
    };

    $('#J_input_count').html(num+'个字');

  }
})

$(function() {
  new TextCount({
  //这边直接传input的节点了,因为属性的赋值都是自动的。
    input:$("#J_input")
  });
})

可以看到我们直接实现一些固定的方法,bind,render就行了。其他的base会自动处理(这里只是简单处理了配置属性的赋值)。

事实上,这边的init,bind,render就已经有了点生命周期的影子,但凡是组件都会具有这几个阶段,初始化,绑定事件,以及渲染。当然这边还可以加一个destroy销毁的方法,用来清理现场。

此外为了方便,这边直接变成了传递input的节点。因为属性赋值自动化了,一般来说这种情况下都是使用getter,setter来处理。这边就不详细展开了。

引入事件机制(观察者模式)

有了base应该说我们编写组件更加的规范化,体系化了。下面我们继续深挖。

还是上面的那个例子,如果我们希望输入字的时候超过5个字就弹出警告。该怎么办呢。

小白可能会说,那简单啊直接改下bind方法:

var TextCount = Base.extend({
  ...
  bind:function(){
    var self = this;
    self.get('input').on('keyup',function(){
      if(self._getNum() > 5){
        alert('超过了5个字了。。。')
      }
      self.render();
    });
  },
  ...
})

的确也是一种方法,但是太low了,代码严重耦合。当这种需求特别特别多,代码会越来越乱。

这个时候就要引入事件机制,也就是经常说的观察者模式。

注意这边的事件机制跟平时的浏览器那些事件不是一回事,要分开来看。

什么是观察者模式呢,官方的解释就不说了,直接拿这个例子来说。

想象一下base是个机器人会说话,他会一直监听输入的字数并且汇报出去(通知)。而你可以把耳朵凑上去,听着他的汇报(监听)。发现字数超过5个字了,你就做些操作。

所以这分为两个部分,一个是通知,一个是监听。

假设通知是 fire方法,监听是on。于是我们可以这么写代码:

var TextCount = Base.extend({
  ...
  bind:function(){
    var self = this;
    self.get('input').on('keyup',function(){
      //通知,每当有输入的时候,就报告出去。
      self.fire('Text.input',self._getNum())
      self.render();
    });
  },
  ...
})

$(function() {
  var t = new TextCount({
    input:$("#J_input")
  });
  //监听这个输入事件
  t.on('Text.input',function(num){
    //可以获取到传递过来的值
    if(num>5){
       alert('超过了5个字了。。。')
    }
  })
})

fire用来触发一个事件,可以传递数据。而on用来添加一个监听。这样组件里面只负责把一些关键的事件抛出来,至于具体的业务逻辑都可以添加监听来实现。没有事件的组件是不完整的。

下面我们看看怎么实现这套事件机制。

我们首先抛开base,想想怎么实现一个具有这套机制的类。


//辅组函数,获取数组里某个元素的索引 index
var _indexOf = function(array,key){
  if (array === null) return -1
  var i = 0, length = array.length
  for (; i < length; i++) if (array[i] === item) return i
  return -1
}

var Event = Class.extend({
  //添加监听
  on:function(key,listener){
    //this.__events存储所有的处理函数
    if (!this.__events) {
      this.__events = {}
    }
    if (!this.__events[key]) {
      this.__events[key] = []
    }
    if (_indexOf(this.__events,listener) === -1 && typeof listener === 'function') {
      this.__events[key].push(listener)
    }

    return this
  },
  //触发一个事件,也就是通知
  fire:function(key){

    if (!this.__events || !this.__events[key]) return

    var args = Array.prototype.slice.call(arguments, 1) || []

    var listeners = this.__events[key]
    var i = 0
    var l = listeners.length

    for (i; i < l; i++) {
      listeners[i].apply(this,args)
    }

    return this
  },
  //取消监听
  off:function(key,listener){

    if (!key && !listener) {
      this.__events = {}
    }
    //不传监听函数,就去掉当前key下面的所有的监听函数
    if (key && !listener) {
      delete this.__events[key]
    }

    if (key && listener) {
      var listeners = this.__events[key]
      var index = _indexOf(listeners, listener)

      (index > -1) && listeners.splice(index, 1)
    }

    return this;
  }
})


var a = new Event()

//添加监听 test事件
a.on('test',function(msg){
  alert(msg)
})

//触发 test事件
a.fire('test','我是第一次触发')
a.fire('test','我又触发了')

a.off('test')

a.fire('test','你应该看不到我了')

实现起来并不复杂,只要使用this.__events存下所有的监听函数。在fire的时候去找到并且执行就行了。

这个时候面向对象的好处就来了,如果我们希望base拥有事件机制。只需要这么写:

var Base = Class.extend(Event,{
  ...
  destroy:function(){
    //去掉所有的事件监听
    this.off()
  }
})
//于是可以
//var a  = new Base()
// a.on(xxx,fn)
//
// a.fire()

是的只要extend的时候多混入一个Event,这样Base或者它的子类生成的对象都会自动具有事件机制。

有了事件机制我们可以把组件内部很多状态暴露出来,比如我们可以在set方法中抛出一个事件,这样每次属性变更的时候我们都可以监听到。

到这里为止,我们的base类已经像模像样了,具有了init,bind,render,destroy方法来表示组件的各个关键过程,并且具有了事件机制。基本上已经可以很好的来开发组件了。

更进一步,richbase

我们还可以继续深挖。看看我们的base,还差些什么。首先浏览器的事件监听还很落后,需要用户自己在bind里面绑定,再然后现在的TextCount里面还存在dom操作,也没有自己的模板机制。这都是需要扩展的,于是我们在base的基础上再继承出一个richbase用来实现更完备的组件基类。

主要实现这些功能:

  • 事件代理:不需要用户自己去找dom元素绑定监听,也不需要用户去关心什么时候销毁。
  • 模板渲染:用户不需要覆盖render方法,而是覆盖实现setUp方法。可以通过在setUp里面调用render来达到渲染对应html的目的。
  • 单向绑定:通过setChuckdata方法,更新数据,同时会更新html内容,不再需要dom操作。

我们看下我们实现richbase后怎么写组件:

var TextCount = RichBase.extend({
  //事件直接在这里注册,会代理到parentNode节点,parentNode节点在下面指定
  EVENTS:{
    //选择器字符串,支持所有jQuery风格的选择器
    'input':{
      //注册keyup事件
      keyup:function(self,e){
        //单向绑定,修改数据直接更新对应模板
        self.setChuckdata('count',self._getNum())

      }
    }
  },
  //指定当前组件的模板
  template:'<span id="J_input_count"><%= count %>个字</span>',
  //私有方法
  _getNum:function(){
    return this.get('input').val().length || 0
  },
  //覆盖实现setUp方法,所有逻辑写在这里。最后可以使用render来决定需不需要渲染模板
  //模板渲染后会append到parentNode节点下面,如果未指定,会append到document.body
  setUp:function(){
    var self = this;

    var input = this.get('parentNode').find('#J_input')
    self.set('input',input)

    var num = this._getNum()
    //赋值数据,渲染模板,选用。有的组件没有对应的模板就可以不调用这步。
    self.render({
      count:num
    })

  }
})

$(function() {
  //传入parentNode节点,组件会挂载到这个节点上。所有事件都会代理到这个上面。
  new TextCount({
    parentNode:$("#J_test_container")
  });
})

/**对应的html,做了些修改,主要为了加上parentNode,这边就是J_test_container

<div id="J_test_container">
  <input type="text" id="J_input"/>
</div>

*/

看下上面的用法,可以看到变得更简单清晰了:

  • 事件不需要自己绑定,直接注册在EVENTS属性上。程序会自动将事件代理到parentNode上。
  • 引入了模板机制,使用template规定组件的模板,然后在setUp里面使用render(data)的方式渲染模板,程序会自动帮你append到parentNode下面。
  • 单向绑定,无需操作dom,后面要改动内容,不需要操作dom,只需要调用setChuckdata(key,新的值),选择性的更新某个数据,相应的html会自动重新渲染。

下面我们看下richebase的实现:


var RichBase = Base.extend({
  EVENTS:{},
  template:'',
  init:function(config){
    //存储配置项
    this.__config = config
    //解析代理事件
    this._delegateEvent()
    this.setUp()
  },
  //循环遍历EVENTS,使用jQuery的delegate代理到parentNode
  _delegateEvent:function(){
    var self = this
    var events = this.EVENTS || {}
    var eventObjs,fn,select,type
    var parentNode = this.get('parentNode') || $(document.body)

    for (select in events) {
      eventObjs = events[select]

      for (type in eventObjs) {
        fn = eventObjs[type]

        parentNode.delegate(select,type,function(e){
          fn.call(null,self,e)
        })
      }

    }

  },
  //支持underscore的极简模板语法
  //用来渲染模板,这边是抄的underscore的。非常简单的模板引擎,支持原生的js语法
  _parseTemplate:function(str,data){
    /**
     * http://ejohn.org/blog/javascript-micro-templating/
     * https://github.com/jashkenas/underscore/blob/0.1.0/underscore.js#L399
     */
    var fn = new Function('obj',
        'var p=[],print=function(){p.push.apply(p,arguments);};' +
        'with(obj){p.push(\'' + str
            .replace(/[\r\t\n]/g, " ")
            .split("<%").join("\t")
            .replace(/((^|%>)[^\t]*)'/g, "$1\r")
            .replace(/\t=(.*?)%>/g, "',$1,'")
            .split("\t").join("');")
            .split("%>").join("p.push('")
            .split("\r").join("\\'") +
        "');}return p.join('');")
    return data ? fn(data) : fn
  },
  //提供给子类覆盖实现
  setUp:function(){
    this.render()
  },
  //用来实现刷新,只需要传入之前render时的数据里的key还有更新值,就可以自动刷新模板
  setChuckdata:function(key,value){
    var self = this
    var data = self.get('__renderData')

    //更新对应的值
    data[key] = value

    if (!this.template) return;
    //重新渲染
    var newHtmlNode = $(self._parseTemplate(this.template,data))
    //拿到存储的渲染后的节点
    var currentNode = self.get('__currentNode')
    if (!currentNode) return;
    //替换内容
    currentNode.replaceWith(newHtmlNode)

    self.set('__currentNode',newHtmlNode)

  },
  //使用data来渲染模板并且append到parentNode下面
  render:function(data){
    var self = this
    //先存储起来渲染的data,方便后面setChuckdata获取使用
    self.set('__renderData',data)

    if (!this.template) return;

    //使用_parseTemplate解析渲染模板生成html
    //子类可以覆盖这个方法使用其他的模板引擎解析
    var html = self._parseTemplate(this.template,data)

    var parentNode = this.get('parentNode') || $(document.body)

    var currentNode = $(html)
    //保存下来留待后面的区域刷新
    //存储起来,方便后面setChuckdata获取使用
    self.set('__currentNode',currentNode)
    parentNode.append(currentNode)
  },
  destroy:function(){

    var self = this
    //去掉自身的事件监听
    self.off()
    //删除渲染好的dom节点
    self.get('__currentNode').remove()
    //去掉绑定的代理事件
    var events = self.EVENTS || {}
    var eventObjs,fn,select,type
    var parentNode = self.get('parentNode')

    for (select in events) {
      eventObjs = events[select]

      for (type in eventObjs) {
        fn = eventObjs[type]

        parentNode.undelegate(select,type,fn)
      }

    }

  }
})

主要做了两件事,一个就是事件的解析跟代理,全部代理到parentNode上面。另外就是把render抽出来,用户只需要实现setUp方法。如果需要模板支持就在setUp里面调用render来渲染模板,并且可以通过setChuckdata来刷新模板,实现单向绑定。

结语

有了richbase,基本上组件开发就没啥问题了。但是我们还是可以继续深挖下去。

比如组件自动化加载渲染,局部刷新,比如父子组件的嵌套,再比如双向绑定,再比如实现ng-click这种风格的事件机制。

当然这些东西已经不属于组件里面的内容了。再进一步其实已经是一个框架了。实际上最近比较流行的react,ploymer还有我们的brix等等都是实现了这套东西。受限于篇幅,这个以后有空再写篇文章详细分析下。

<!-- Baidu Button BEGIN -->
 
<script type="text/javascript">// <![CDATA[ window._bd_share_config = { "common": { "bdSnsKey": {}, "bdText": "", "bdMini": "1", "bdMiniList": false, "bdPic": "", "bdStyle": "0", "bdSize": "16" }, "share": {} }; with (document) 0[(getElementsByTagName('head')[0] || body).appendChild(createElement('script')).src = 'http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion=' + ~(-new Date() / 36e5)]; // ]]></script><script id="bdshare_js" type="text/javascript"></script><script id="bdshell_js" type="text/javascript"></script><script type="text/javascript">// <![CDATA[ document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000) // ]]></script><!-- Baidu Button END -->
猜你在找
<script type="text/javascript" src="http://csdnimg.cn/jobreco/job_reco.js"></script><script type="text/javascript">// <![CDATA[ csdn.position.showEdu({ sourceType: "blog", searchType: "detail", searchKey: "44451481", username: "", recordcount: "5", containerId: "adCollege" //容器DIV的id。 }); // ]]></script>
<script type="text/javascript" src="http://c.csdnimg.cn/jobreco/job_reco.js"></script><script type="text/javascript">// <![CDATA[ csdn.position.show({ sourceType: "blog", tplType: "blogDetail", searchType: "detail", searchKey: "44451481", username: "", containerId: "job_blog_reco" }); // ]]></script>
<script type="text/javascript">// <![CDATA[ var searchtitletags = 'javascript组件开发方式' + ',' + ''; searchService({ index: 'blog', query: searchtitletags, from: 5, size: 5, appendTo: '#res', url: 'recommend', his: 2, client: "blog_cf_enhance", tmpl: '<dd style="background:url(http://static.blog.csdn.net/skin/default/images/blog-dot-red3.gif) no-repeat 0 10px;"><a href="#{ url }" title="#{ title }" strategy="#{ strategy }">#{ title }</a></dd>' }); // ]]></script>
<script type="text/javascript">// <![CDATA[ new Ad(4, 'ad_cen'); // ]]></script>
查看评论
 
 
 
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
<script type="text/javascript">// <![CDATA[ var fileName = '44451481'; var commentscount = 0; var islock = false // ]]></script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/comment.js"></script>
 
<script type="text/javascript">// <![CDATA[ new Ad(5, 'ad_bot'); // ]]></script>
 
快速回复 TOP
<script type="text/javascript">// <![CDATA[ $(function () { $("#ad_frm_0").height("90px"); setTimeout(function(){ $("#ad_frm_2").height("200px"); },1000); /* if($("#comment_content").length>0) { $("#quick-reply").show(); $("#quick-reply").click(function(){ setEditorFocus(); }); } var d_top = $('#d-top-a'); document.onscroll = function () { var scrTop = (document.body.scrollTop || document.documentElement.scrollTop); if (scrTop > 500) { d_top.show(); } else { d_top.hide(); } } $('#d-top-a').click(function () { scrollTo(0, 0); this.blur(); return false; }); */ }); // ]]></script>
 
 
<script type="text/javascript">// <![CDATA[ $(function(){ setTimeout(function(){ $(".comment_body:contains('回复')").each(function(index,item){ var u=$(this).text().split(':')[0].toString().replace("回复","") var thisComment=$(this); if(u) { $.getJSON("https://passport.csdn.net/get/nick?callback=?", {users: u}, function(a) { if(a!=null&&a.data!=null&&a.data.length>0) { nick=a.data[0].n; if(u!=nick) { thisComment.text(thisComment.text().replace(u,nick)); } } }); } }); },200); setTimeout(function(){ $("a img[src='http://js.tongji.linezing.com/stats.gif']").parent().css({"position":"absolute","left":"50%"}); },300); }); function loginbox(){ var $logpop=$("#pop_win"); $logpop.html('<iframe src="https://passport.csdn.net/account/loginbox?service=http://static.blog.csdn.net/callback.htm" frameborder="0" height="600" width="400" scrolling="no"></iframe>'); $('#popup_mask').css({ opacity: 0.5, width: $( document ).width() + 'px', height: $( document ).height() + 'px' }); $('#popup_mask').css("display","block"); $logpop.css( { top: ($( window ).height() - $logpop.height())/ 2 + $( window ).scrollTop() + 'px', left:($( window ).width() - $logpop.width())/ 2 } ); setTimeout( function () { $logpop.show(); $logpop.css( { opacity: 1 } ); }, 200 ); $('#popup_mask').unbind("click"); $('#popup_mask').bind("click", function(){ $('#popup_mask').hide(); var $clopop = $("#pop_win"); $("#common_ask_div_sc").css("display","none"); $clopop.css( { opacity: 0 } ); setTimeout( function () { $clopop.hide(); }, 350 ); return false; }); } // ]]></script>
 
个人资料
 
 
    • 访问:430864次
    • 积分:5329
    • 等级:
       
      积分:5329
    • 排名:第2195名
    • 原创:23篇
    • 转载:478篇
    • 译文:0篇
    • 评论:59条
文章搜索
<script type="text/javascript">// <![CDATA[ $(function () { $("#btnSubmit").click(function () { search(); }); $("#frmSearch").submit(function () { search(); return false; }); function search() { var url = "http://so.csdn.net/so/search/s.do?q=" + encodeURIComponent($("#inputSearch").val()) + "&u=" + username + "&t=blog"; window.location.href = url; } }); // ]]></script>
文章分类
文章存档 <script type="text/javascript" src="http://c.csdnimg.cn/rabbit/cnick/cnick.js"></script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/newblog.min.js"></script><script type="text/javascript" src="http://medal.blog.csdn.net/showblogmedal.ashx?blogid=1184119"></script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/JavaScript1.js"></script><script type="text/javascript" src="http://passport.csdn.net/content/loginbox/login.js"></script><script type="text/javascript">// <![CDATA[ document.write("<img src=http://counter.csdn.net/pv.aspx?id=24 border=0 width=0 height=0>"); // ]]></script><script type="text/javascript" src="http://www.csdn.net/ui/scripts/Csdn/counter.js"></script><script type="text/javascript" src="http://ad.csdn.net/scripts/ad-blog.js"></script><script type="text/javascript" src="http://zz.csdn.net/js/count.js"></script><script type="text/javascript">// <![CDATA[ $(function () { function __get_code_toolbar(snippet_id) { return $("<a href='https://code.csdn.net/snippets/" + snippet_id + "' target='_blank' title='在CODE上查看代码片' style='text-indent:0;'><img src='https://code.csdn.net/assets/CODE_ico.png' width=12 height=12 alt='在CODE上查看代码片' style='position:relative;top:1px;left:2px;'/></a>" + "<a href='https://code.csdn.net/snippets/" + snippet_id + "/fork' target='_blank' title='派生到我的代码片' style='text-indent:0;'><img src='https://code.csdn.net/assets/ico_fork.svg' width=12 height=12 alt='派生到我的代码片' style='position:relative;top:2px;left:2px;'/></a>"); } $("[code_snippet_id]").each(function () { __s_id = $(this).attr("code_snippet_id"); if (__s_id != null && __s_id != "" && __s_id != 0 && parseInt(__s_id) > 70020) { __code_tool = __get_code_toolbar(__s_id); $(this).prev().find(".tools").append(__code_tool); } }); }); // ]]></script>
<!--new top--> <script id="csdn-toolbar-id" type="text/javascript" src="http://static.csdn.net/public/common/toolbar/js/toolbar.js"></script><!--new top--><script type="text/javascript" language="JavaScript" src="http://c.csdnimg.cn/comm_ask/js/libs/wmd.js"></script><script type="text/javascript" language="JavaScript" src="http://c.csdnimg.cn/comm_ask/js/libs/showdown.js"></script><script type="text/javascript" language="JavaScript" src="http://c.csdnimg.cn/comm_ask/js/libs/prettify.js"></script><script type="text/javascript" language="JavaScript" src="http://c.csdnimg.cn/comm_ask/js/apps/ask_float_block.js"></script>
分享到:
评论

相关推荐

    VueUI组件开发环境

    在开发组件时,Vue DevTools可以帮助你实时查看组件状态、事件监听器等,提高开发效率。 8. **ES6语法**:Vue.js推荐使用ES6语法,因此理解箭头函数、解构赋值、模板字符串、类等现代JavaScript特性是非常必要的。 ...

    JavaScript组件开发完整示例

    JavaScript组件开发是Web应用开发中的重要一环,它允许开发者以模块化的方式组织代码,提高代码的可维护性和复用性。本实例将通过构建一个简单的TAB组件来讲解JavaScript组件开发的基本技巧。 首先,理解组件的基本...

    刘光《ArcGIS Server JavaScript API开发GeoWeb 2.0应用》书的源代码

    《ArcGIS Server JavaScript API开发GeoWeb 2.0应用》一书由刘光和唐大仕共同撰写,主要探讨了如何使用ArcGIS Server的JavaScript API来构建GeoWeb 2.0应用程序。GeoWeb 2.0是地理信息系统(GIS)在互联网上的一个...

    JavaScript组件化开发实战——TodoMVC

    TodoMVC旨在用各种框架实现TodoList的增、删、改、查功能,麻雀虽小,五脏俱全,是供小伙伴学习、练习、再学习的好例子。 虽然官网上有各种实现...为此,本猿抛砖引玉,用RequireJs+jQuery实现一个组件化的TodoMVC。

    Z01866Web前端开发技术 HTML、CSS、JavaScript 第2版.pdf

    Z01866Web前端开发技术 HTML、CSS、JavaScript 第2版

    现代JavaScript教程

    自ECMAScript 6(简称ES6)以来,JavaScript引入了许多新特性,如箭头函数、模板字符串、let和const、类和模块等。掌握这些新特性能提升代码的可读性和简洁性。 六、DOM操作 在Web开发中,JavaScript常常用于操作...

    JavaScript征途

    《JavaScript征途》内容全面,由浅入深,包括6篇21章,主要内容包括:JavaScript语言的基本特性,开发简单的JavaScript程序,JavaScript语法体系,数据、数据类型和变量,运算符和表达式,语句和结构,JavaScript...

    VS2015安装证书,JavaScript_ProjectSystem.msi,JavaScript_LanguageService.msi

    VS2015是Microsoft Visual Studio 2015的...总之,VS2015的安装证书和特定组件对于顺畅进行JavaScript开发至关重要。确保所有必需的组件完整无损,并正确安装,将有助于提升开发效率和避免安装过程中可能出现的问题。

    课程设计-基于JavaScript开发的微信图书推荐小程序源码+项目说明.zip

    课程设计-基于JavaScript开发的微信图书推荐小程序源码+项目说明.zip课程设计-基于JavaScript开发的微信图书推荐小程序源码+项目说明.zip课程设计-基于JavaScript开发的微信图书推荐小程序源码+项目说明.zip课程设计...

    xmlplus是一个JavaScript框架用于快速开发前后端项目

    总的来说,xmlplus框架以其组件化设计和强大的功能集,为JavaScript开发提供了高效的解决方案,无论是对于前端界面的构建,还是后端服务的开发,都能提供有力的支持。通过深入理解并熟练运用xmlplus,开发者可以打造...

    javascript开发帮助文档

    javascript开发帮助文档.不是api ,希望大家不要弄错了。

    [JavaScript权威指南(第6版)]

    本书第6版涵盖了HTML5和ECMAScript 5,很多章节完全重写,增加了当今Web开发的最佳实践的内容,新增的章节包括jQuery、服务器端JavaScript、图形编程以及 JavaScript式的面向对象。本书不仅适合初学者系统学习,也...

    JavaScript_Open Web Components指导开发Web组件的工具和库.zip

    随着Web Components技术的发展,JavaScript已经能够帮助开发者创建可复用、自包含的Web组件,这些组件可以独立于任何特定框架或库运行。"JavaScript_Open Web Components指导开发Web组件的工具和库.zip"这个压缩包...

    原生 JavaScript 开发的省市区多级联动组件

    原生 JavaScript 开发的省市区多级联动组件

    用于单独开发和测试UI组件的沙箱_TypeScript_JavaScript_下载.zip

    "用于单独开发和测试UI组件的沙箱_TypeScript_JavaScript_下载.zip" 提供的正是这样一个工具,它支持使用TypeScript和JavaScript两种语言,为React UI组件的开发提供了一个安全、独立的环境。 TypeScript是...

    基于JavaScript的烟花特效OH组件开发大赛源码分享

    本项目是一款基于JavaScript的烟花特效OH组件,旨在提供一场视觉盛宴。源码包含共计50个文件,涵盖13个JSON配置文件、6个JavaScript文件、5个XML文件、3个Git忽略文件、3个Gradle构建文件、3个Java类文件、3个PNG...

    ReactNative用javascript开发移动应用.中文完整版.pdf

    React Native将React.js的组件化开发方式引入到移动应用开发中,使得Web开发者可以运用他们熟悉的JavaScript和React技能,同时享受到接近原生应用的性能。它的核心是基于React组件库,开发者可以通过定义可重用的...

    WeChatassembly小程序组件开发

    在本文中,我们将深入探讨JavaScript开发在微信小程序中的应用,以及如何利用WeChat-assembly进行高效的小程序组件开发。 首先,微信小程序是一种轻量级的应用形态,它允许开发者在无需安装的情况下,通过微信平台...

    JavaScript基于WebGL技术实现的三维BS端开发平台(含Cesium 的核心操作库+UI组件库).zip

    JavaScript基于WebGL技术实现的三维BS端开发平台(含Cesium 的核心操作库+UI组件库).zipJavaScript基于WebGL技术实现的三维BS端开发平台(含Cesium 的核心操作库+UI组件库).zipJavaScript基于WebGL技术实现的三维BS端...

Global site tag (gtag.js) - Google Analytics