《JavaScript修炼之道》读书笔记

来源:互联网 发布:mac正在关闭图库 编辑:程序博客网 时间:2024/06/06 00:47

1、参考书目

入门:《JavaScript DOM编程艺术》第二版

进阶:《JavaScript高级程序设计》第二版、《JavaScript编程精粹》

《JavaScript权威指南》

2、引言

Node.js是CommonJS的一个部分实现

本书代码下载:

http://github.com/tdd/pragmatic-javascript

https://github.com/tdd/pragmatic-javascript/archive/master.zip

http://media.pragprog.com/titles/pg_js/code/pg_js-code.tgz

介绍的框架包括:Prototype、jQuery、MooTools、YUI、Dojo、ExtJS

 

3、利用事件委托

  • 优先使用事件委托
  • 元素共享同一个行为,最好在DOM高层次监听事件,这样可以节省内存和CPU时间

dom/delegation.html

<ul id="items">  <!-- We will insert togglers in each LI using JS -->  <li><div><p>Data 1</p><p>Data 2</p></div></li>  <li><div><p>Data 1</p><p>Data 2</p></div></li>  <li><div><p>Data 1</p><p>Data 2</p></div></li>  <!-- Potentially lots more elements here… --></ul>

dom/delegation.js(Prototype)

document.observe('dom:loaded', function() {  $('items').observe('click', function(e) {     var trigger = e.findElement('a.toggler');     if (!trigger) return;    e.stop();    var content = trigger.up('p').next('div');    if (!content) return;    content.toggle();    trigger.update(content.visible() ? 'Close' : 'Open');    trigger.blur();  });  $('items').select('li').each(function(item) {    item.insert({ top: '<p><a class="toggler" href="#">Open</a></p>' });    item.down('div').hide();  });});

4、模拟后台处理

  • 利用HTML5的Web Workers实现多线程
  • 分解成多个小任务,利用setTimerout和clearTimeout模拟后台处理
var handle = windows.setTimeout(callback, intervalInMs)window.clearTimeout(hanle);

参考https://github.com/madrobby/emile 50行小程序实现精确定时器

(function(emile, container){  var parseEl = document.createElement('div'),    props = ('backgroundColor borderBottomColor borderBottomWidth borderLeftColor borderLeftWidth '+    'borderRightColor borderRightWidth borderSpacing borderTopColor borderTopWidth bottom color fontSize '+    'fontWeight height left letterSpacing lineHeight marginBottom marginLeft marginRight marginTop maxHeight '+    'maxWidth minHeight minWidth opacity outlineColor outlineOffset outlineWidth paddingBottom paddingLeft '+    'paddingRight paddingTop right textIndent top width wordSpacing zIndex').split(' ');  function interpolate(source,target,pos){ return (source+(target-source)*pos).toFixed(3); }  function s(str, p, c){ return str.substr(p,c||1); }  function color(source,target,pos){    var i = 2, j, c, tmp, v = [], r = [];    while(j=3,c=arguments[i-1],i--)      if(s(c,0)=='r') { c = c.match(/\d+/g); while(j--) v.push(~~c[j]); } else {        if(c.length==4) c='#'+s(c,1)+s(c,1)+s(c,2)+s(c,2)+s(c,3)+s(c,3);        while(j--) v.push(parseInt(s(c,1+j*2,2), 16)); }    while(j--) { tmp = ~~(v[j+3]+(v[j]-v[j+3])*pos); r.push(tmp<0?0:tmp>255?255:tmp); }    return 'rgb('+r.join(',')+')';  }    function parse(prop){    var p = parseFloat(prop), q = prop.replace(/^[\-\d\.]+/,'');    return isNaN(p) ? { v: q, f: color, u: ''} : { v: p, f: interpolate, u: q };  }    function normalize(style){    var css, rules = {}, i = props.length, v;    parseEl.innerHTML = '<div style="'+style+'"></div>';    css = parseEl.childNodes[0].style;    while(i--) if(v = css[props[i]]) rules[props[i]] = parse(v);    return rules;  }      container[emile] = function(el, style, opts, after){    el = typeof el == 'string' ? document.getElementById(el) : el;    opts = opts || {};    var target = normalize(style), comp = el.currentStyle ? el.currentStyle : getComputedStyle(el, null),      prop, current = {}, start = +new Date, dur = opts.duration||200, finish = start+dur, interval,      easing = opts.easing || function(pos){ return (-Math.cos(pos*Math.PI)/2) + 0.5; };    for(prop in target) current[prop] = parse(comp[prop]);    interval = setInterval(function(){      var time = +new Date, pos = time>finish ? 1 : (time-start)/dur;      for(prop in target)        el.style[prop] = target[prop].f(current[prop].v,target[prop].v,easing(pos)) + target[prop].u;      if(time>finish) { clearInterval(interval); opts.after && opts.after(); after && setTimeout(after,1); }    },10);  }})('emile', this);

dom/background.js

var CHUNK_INTERVAL = 25; // ms.  var running = false, progress = 0, processTimer;  function runChunk() {    window.clearTimeout(processTimer);    processTimer = null;    if (!running) return;    // Some work chunk.  Let's simulate it:    for (var i = 0; i < 10000; i += (Math.random() * 5).round())      ;    ++progress;    updateUI(); // See source archive -- just updates a progressbar    if (progress < 100) {      processTimer = window.setTimeout(runChunk, CHUNK_INTERVAL);    } else {      progress = 0, running = false;    }  }    function toggleProcessing() {    running = !running;    if (running) {      processTimer = window.setTimeout(runChunk, CHUNK_INTERVAL);    }  }

5、打造漂亮的tooltip

用CSS属性设置tooltip元素为默认隐藏,并在其内容标签上加:hover选择器来恢复显示。

但这种方式在IE6不起作用,因为IE6只允许<a>元素上有:hover。只能手动编写脚本,响应mouseover和mouseout。

作者推荐Prototype的Prototip2库http://www.nickstakenburg.com/projects/prototip2/

4{5OB7)DWK}Y6YWLGWM3CCA

ui/tooltips/index.html

<li tabindex="1">    <span class="name">Capacity: 1.5 TB</span>    <div class="tooltip" >      <p><strong>1.5 Terabyte = 1,536 Gigabytes</strong></p>      <p>Enough for 50,000 songs, 1,000 DivX movies, 100,000        high-definition photos, hundreds of iDVD projects and        plenty of backup space left.</p>    </div>  </li>

ui/tooltips/tooltips.css

#files li { position: relative; }#files li .tooltip {  position: absolute; top: 8px; left: 120px; width: 24em;  z-index: 1; display: none;  /* IE6 doesn't know li:hover, so we need to toggle via JS,     therefore avoiding in-rule display: none */  _display: block;  border: 1px solid gray;   background: #fffdc3 url(bg_tooltip.png) top left repeat-x;}#files li:hover .tooltip,#files li:focus .tooltip { display: block; }

ui/tooltips/tooltips.js

function toggle(reveal, e) {    var trigger = e.findElement('li'),      tooltip = trigger && trigger.down('.tooltip');    if (!tooltip) return;    tooltip[reveal ? 'show' : 'hide']();  }    document.observe('dom:loaded', function() {    var isIE6 = Prototype.Browser.IE &&      undefined === document.body.style.maxHeight;    if (!isIE6) return;    var files = $('files'), tooltips = files && files.select('.tooltip');    if (!files || 0 == tooltips.length) return;    tooltips.invoke('hide');    files.observe('mouseover', toggle.curry(true)).      observe('mouseout', toggle.curry(false));  });

 

6、友好的弹窗

用<a>链接到要弹出的内容(href=,target=”_blank”),然后在链接上挂上JavaScript代码。这样可以解决禁止弹窗、屏幕阅读器(视觉障碍者使用)、搜索引擎的访问问题。

ui/popups/index.html

<p>  The great thing about <a class="popup" target="_blank"  href="http://pragprog.com/titles/pg_js">Pocket Guide to JavaScript</a>  is that it focuses on a bunch of specific, useful tasks.</p>

ui/popus/popus.js

var POPUP_FEATURES = 'status=yes,resizable=yes,scrollbars=yes,' +     'width=800,height=500,left=100,top=100';    function hookPopupLink(e) {    var trigger = e.findElement('a.popup');    if (!trigger) return;    e.stop(); trigger.blur();    var wndName = trigger.readAttribute('target') ||      ('wnd' + trigger.identify());    window.open(trigger.href, wndName, POPUP_FEATURES).focus();  }    document.observe('click', hookPopupLink);

7、光箱特效

u=1109846169,3307702938&fm=21&gp=0

利用FancyBox jQuery插件

ui/lightbox/lightbox.js

$('#thumbnails a').fancybox({    zoomSpeedIn: 300, zoomOpacity: true, overlayColor: '#000',    overlayOpacity: 0.6  });

8、无限翻页

ui/infinite/infinite.js

function lowEnough() {    var pageHeight = Math.max(document.body.scrollHeight,      document.body.offsetHeight);    var viewportHeight = window.innerHeight ||      document.documentElement.clientHeight ||       document.body.clientHeight || 0;    var scrollHeight = window.pageYOffset ||      document.documentElement.scrollTop ||       document.body.scrollTop || 0;    // Trigger for scrolls within 20 pixels from page bottom    return pageHeight - viewportHeight - scrollHeight < 20;  }  function checkScroll() {    if (!lowEnough()) return pollScroll();    $('spinner').show();    new Ajax.Updater('posts', 'more.php', {      method: 'get', insertion: 'bottom',      onComplete: function() { $('spinner').hide(); },      onSuccess: pollScroll    });  }    function pollScroll() { setTimeout(checkScroll, 100); }    pollScroll();

9、载入内容时保持显示区域的位置不变

ui/viewport/index.html

<h2>Comments</h2><div id="extraComments">  <a id="loadKnownComments" href="?with_known_comments">See previous    comments you already know about</a></div><h3>Comment 5</h3>

ui/viewport/viewport.js

function loadKnownComments(e) {    e.stop();    var zone = $('extraComments'), ref = zone.next('h3');    var upd = new Ajax.Request('known_comments.html', {      method: 'get',      onSuccess: function(res) {        var orig = ref.cumulativeOffset().top -          document.viewport.getScrollOffsets().top;        zone.insert({ before: res.responseText });        window.scrollTo(0, ref.cumulativeOffset().top - orig);      }    });  }    document.observe('dom:loaded', function() {    var loader = $('loadKnownComments');    loader && loader.observe('click', loadKnownComments);  });

10、提供输入长度的反馈

  • 对keyup和keypress监听,以及对非字符键(删除、剪切、粘贴)做出响应。没必要监听keydown
  • 为避免每次按键都重复计算最大长度,我们在初始化时把它缓存起来。为了把最大长度和输入区域关联起来,我们用了JS关联数组,这比用属性轻便点

form/feedback/index.html

<p>    <label for="edtDescription">Description</label>    <textarea id="edtDescription" name="description" cols="40"     rows="5" class="maxLength200"></textarea>  </p>

form/feedback/feedback.js

var maxLengths = {};    function bindMaxLengthFeedbacks() {    var mlClass, maxLength, feedback;    $$('*[class^=maxLength]').each(function(field) {      field.up('p').addClassName('lengthFeedback');      mlClass = field.className.match(/\bmaxLength(\d+)\b/)[0];      maxLength = parseInt(mlClass.replace(/\D+/g, ''), 10);      feedback = new Element('span', { 'class': 'feedback' });      maxLengths[field.identify()] = [maxLength, feedback];      updateFeedback(field);      field.observe('keyup', updateFeedback).        observe('keypress', updateFeedback);      feedback.clonePosition(field, { setHeight: false,        offsetTop: field.offsetHeight + 2 });      field.insert({ after: feedback });    });  }    function updateFeedback(e) {    var field = e.tagName ? e : e.element();    var current = field.getValue().length,      data = maxLengths[field.id], max = data[0],      delta = current < max ? max - current : 0;    data[1].update('Remaining: ' + delta);    if (current > max) {      field.setValue(field.getValue().substring(0, max));    }  }  document.observe('dom:loaded', bindMaxLengthFeedbacks);

11、同时选择或反选多个checkbox

form/checklist/index_for_book.html

<table id="mailbox">  <thead>    <tr>      <th><input type="checkbox" id="toggler" /></th>      <th>Subject</th>      <th>Date</th>      <!-- From, Size, Attachments… -->    </tr>  </thead>  <tbody>    <tr>      <td><input type="checkbox" name="mail_ids[]" value="1" /></td>      <td>Happy new year!</td>      <td>Jan 1, 2010 00:03am</td>      <!---->    </tr>    <!-- More rows… -->  </tbody></table>

form/checklist/checklist.js

function toggleAllCheckboxes() {    var scope = this.up('table').down('tbody'), boxes = scope &&      scope.select('tr input[type="checkbox"]:first-of-type');    var refChecked = this.checked;    (boxes || []).each(function(box) { box.checked = refChecked; });  }    document.observe('dom:loaded', function() {    $('toggler').observe('click', toggleAllCheckboxes);  });

12、表单验证

检验未填的必填项

form/validation101/validation101.js

function checkForm(e) {    var firstOffender, value;    this.select('.required').each(function(field) {      value = field.getValue();      if (value && !value.blank()) {        field.up('p').removeClassName('missing');      } else {        firstOffender = firstOffender || field;        field.up('p').addClassName('missing');      }    });    if (firstOffender) { e.stop(); firstOffender.focus(); }  }    document.observe('dom:loaded', function() {    $('registration').observe('submit', checkForm);  });

检查特定格式的输入域

form/validation102/validation102.js

var FIELD_PATTERNS = {    integer: /^\d+$/,    number: /^\d+(?:\.\d+)?$/,    email: /^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$/i  };    function checkField(field) {    var value = $F(field).toString().strip();    for (var pattern in FIELD_PATTERNS) {      if (!field.hasClassName(pattern)) continue;      if (!FIELD_PATTERNS[pattern].test(value)) return false;    }    return true;  }

和服务器端通信,监测登录名域

form/validation_ajax/validation_ajax.js

document.observe('dom:loaded', function checkLogin() {    var feedback = $('user_login').next('.feedback'),      spinner = $('user_login').next('.spinner');    new Field.Observer('user_login', 0.8, function(_, value) {      if (value.length < 2) return;      feedback.hide(); spinner.show();      new Ajax.Request('check_login.php', {        method: 'get', parameters: { login: value },        onComplete: function(res) {          if (Ajax.activeRequestCount > 1) return;          if (res.request.success() && res.status) {            feedback.update('Login available!').removeClassName('ko');          } else {            feedback.update('Login taken!').addClassName('ko');          }          spinner.hide(); feedback.show();        },      });    });

form/validation_ajax/check_login.php

sleep(rand(5, 10) / 10.0); // Simulate intarwebs delay…$RESERVED = array('bob', 'doudou', 'tdd', 'meshak', 'ook');$login = isset($_GET['login']) ? $_GET['login'] : '';$response = in_array($login, $RESERVED) ? '422 Conflict' : '202 Accepted';header('HTTP/1.1 ' . $response);

13、在表单中提供动态的帮助tooltip

form/tooltips/index.html

<p>    <label for="user_login">      Login*      <span class="tooltip" style="display: none;">        Logins must be unique, at least 3 characters long,        and may only use letters, numbers, white space,        hyphens, underscores and periods.      </span>    </label>    <input type="text" id="user_login" name="user[login]"     class="required text" />  </p>

form/tooltips/tooltips.css

#registration { font-family: sans-serif; }#registration p { margin: 0 0 0.5em;}/* START:main */#registration label { float: left; width: 6em; position: relative; zoom: 1; }#registration input.text { width: 14em; }#registration .tooltip {  display: block; position: absolute; left: 24em; top: 0;  padding: 0.35em 0.5em 0.35em 2em; width: 15em;  border: 1px solid silver;  color: gray; font-size: 80%;  background: #ffc url(lightbulb.png) 0.5em 0.3em no-repeat;}

form/tooltips/tooltips.js

document.observe('dom:loaded', function() {    var attr = Prototype.Browser.IE ? 'htmlFor' : 'for';    function showTooltip() {      var tooltip = $$('label['+attr+'="'+this.id+'"] .tooltip').first();      tooltip && tooltip.show();    }    function hideTooltip() {      var tooltip = $$('label['+attr+'="'+this.id+'"] .tooltip').first();      tooltip && tooltip.hide();    }        $('registration').getInputs().invoke('observe', 'focus', showTooltip).      invoke('observe', 'blur', hideTooltip);  });

14、自动完成输入

Prototype的Sctipt.aculo.us控件

form/autocompletion/index.html

<div class="p" id="local">    <label for="edtCachedSearch">Local search:</label>    <input type="text" id="edtCachedSearch" name="search" type="text" />    <div class="completions"></div>  </div>  <div class="p">    <label for="edtAjaxSearch">Ajax search:</label>    <input type="text" id="edtAjaxSearch" name="search" type="text" />    (capitals of the world)    <div class="completions"></div>  </div>

form/autocompletion/autocompletion.css

.completions {  border: 1px solid silver; background: white; font-size: 80%; z-index: 2;}.completions ul { margin: 0; padding: 0; list-style-type: none; }.completions li { line-height: 1.5em; white-space: nowrap;                   overflow: hidden; }.completions li.selected { background: #ffa; }.completions strong { color: green; }

form/autocompletion/autocompletion.js

var FREQUENT_SEARCHES = [    'JavaScript', 'JavaScript frameworks', 'Prototype', 'jQuery', 'Dojo',    'MooTools', 'Ext', 'Ext JS', 'script.aculo.us', 'Scripty2', 'Ajax',    'XHR', '42'  ];    function initLocalCompletions() {    var field = $('edtCachedSearch'), zone = field.next('.completions');    new Autocompleter.Local(field, zone, FREQUENT_SEARCHES,                             { fullSearch: true });  }    function initAjaxCompletions() {    var field = $('edtAjaxSearch'), zone = field.next('.completions');    new Ajax.Autocompleter(field, zone, 'autocomplete.php', {      method: 'get', paramName: 'search' });  }

15、多文件自动上传

HTML5之前使用Base64编码,每个上传文件膨胀33%

form/uploads/index.html

<form method="post" action="server.php" enctype="multipart/form-data">  <ul id="uploads"></ul>  <p><input type="file" name="files[]" id="filSelector" /></p>  <p><input type="submit" value="Send these files" /></p></form>

form/uploads/uploads.js

var ICONS = $H({ word: $w('doc docx'), image: $w('jpg jpeg gif png') });    function getFileClass(fileName) {    var ext = (fileName.match(/\.(.+?)$/) || [])[1].toString().toLowerCase();    var icon = ICONS.detect(function(pair) { return pair[1].include(ext); });    return (icon || [])[0];  }    function handleQueueRemoval(e) {    var trigger = e.findElement('button');    trigger && trigger.up('li').remove();  }    function queueFile() {    var fileName = $F(this), clone = this.cloneNode(true);    var item = new Element('li', { 'class': getFileClass(fileName) });    $(clone).observe('change', queueFile).setValue('');    this.parentNode.appendChild(clone);    item.appendChild(this);    item.appendChild(document.createTextNode(fileName));    item.insert('<button><img src="remove.png" alt="Remove" /></button>');    $('uploads').appendChild(item);  }    document.observe('dom:loaded', function() {    $('filSelector').observe('change', queueFile);    $('uploads').observe('click', handleQueueRemoval);  });

16、使用JSON-P

JSON-P的传输主要依赖于动态生成的<script>标签,所以传输数据可以不限于同一来源。

jsonp和ajax完全是两个概念,可以说jsonp出现的理由就是弥补ajax无法跨域访问的缺陷而出现的。jsonp返回的数据并不是json,而是javascrip。

参考文章:说说JSON和JSONP   初识jsonp

server/jsonp/jsonp.js

function injectData(data) {    var ref = $('sysInfo').down('tbody tr:last-child'), row = new Element('tr'), key;    ref.select('td').each(function(cell) {      row.appendChild($(cell.cloneNode(true)).update(data[cell.className]));    });    ref.insert({ after: row });  }  window.injectData = injectData;    function loadJSONPBasic(e) {    e.stop(); this.blur();    document.documentElement.firstChild.appendChild(      new Element('script', { type: 'text/javascript',        src: this.href + '&r=' + Math.random() }));  }    function loadJSONP(e) {    e.stop(); this.blur();    var script = new Element('script', { type: 'text/javascript',      src: this.href });    script.src += ('&r=' + script.identify());    script.observe('load', Element.remove.curry(script));    document.documentElement.firstChild.appendChild(script);  }    document.observe('dom:loaded', function() {    $('triggerJSONP').observe('click', loadJSONP);  });

随机参数是为了避免浏览器缓存而添加的,为了保证代码质量,最好先检查下一URI,以决定用&还是?(这个参数是否是第一个参数)

在浏览器载入之后将其删除,以免DOM变得过大

17、跨域“Ajax”1

跨域的方法:

  • 通过服务器端代理来载入数据
  • 跨来源资源共享(Cross-Origin Resource Sharing,CORS)。XHR2就是用的CORS,这也是W3C指定的跨域数据请求的方式
  • 使用JSON-P,或者同时使用动态/隐藏表单和<iframe>

server/crossdomain1/crossdomain1.js

// 同时使用动态生成的表单和<iframe>  function loadUsingDF1(e) {    e.stop(); this.blur();    var warp = new Element('iframe', { name: '__blackhole' });    warp.setStyle('width: 0; height: 0; border: 0');    document.body.appendChild(warp);    warp.observe('load', function() {      $('responses').insert('<p>OK, posted.</p>');    });    var form = new Element('form', { method: 'post', action: this.href,      target: '__blackhole' });    form.submit();  }    // 使用得到204响应的动态生成的表单  function loadUsingDF2(e) {    e.stop(); this.blur();    var form = new Element('form', { method: 'post', action: this.href });    form.submit();    Element.insert.defer('responses', '<p>OK, posted.</p>');  }    // 使用服务器端协议  function loadUsingSSP(e) {    e.stop(); this.blur();    new Ajax.Updater({ success: 'responses' }, 'ssp.php', {      method: 'get', parameters: { uri: this.href }, insertion: 'bottom'    });  }    // 使用CORS兼容的XMLHttpRequest  function loadUsingXHR(e) {    e.stop(); this.blur();    new Ajax.Updater({ success: 'responses' }, this.href, {      method: 'get', insertion: 'bottom'    });  }

18、跨域“Ajax”2

YQL并不是云数据库的一部分,它是一个严格的查询处理托管服务。另外,这也意味着YQL不受单独的数据资源限制,甚至不限制应用于雅虎的自身产品。YQL可以操作任何第三方数据源,只要对方是一种常见的格式,如RSS, ATOM, JSON, XML,等等。

// 使用之前那种普通的JSON-P  function loadUsingJSONP(e) {    e.stop(); this.blur();    window.jsonpCallback = function jsonpCallback(data) {      $('responses').update(data.payload.escapeHTML());    };    document.documentElement.firstChild.appendChild(      new Element('script', { type: 'text/javascript',        src: this.href + '?r=' + Math.random() + '&callback=jsonpCallback' }));  }    // 使用JSON-P-X形式的YQL html表(XML格式的JSON-P)  function loadUsingYQLget(e) {    e.stop(); this.blur();    window.yqlCallback = function yqlCallback(data) {      $('responses').update('<ul>' + data.results.map(function(td) {        return '<li>' + td.replace(/<\/?(?:td|p)[^>]*>/g, '').          replace(/href="/g, 'href="http://github.com') + '</li>';      }).join("\n") + '</ul>');    };    var url = this.href, xpath = "//*[@class='title']",      yql = 'select * from html where url="' + url + '" and xpath="' + xpath + '"',      data = { q: yql, format: 'xml', callback: 'yqlCallback' };    document.documentElement.firstChild.appendChild(      new Element('script', { type: 'text/javascript',        src: 'http://query.yahooapis.com/v1/public/yql?' + Object.toQueryString(data) +          '&r=' + Math.random()    }));  }    // 使用JSON-P形式的YQL htmlpost表  function loadUsingYQLpost(e) {    e.stop(); this.blur();    window.yqlCallback = function yqlCallback(data) {      $('responses').update(data.query.results.postresult.p.join("<br/>"));    };    var post = Object.toQueryString({ foo: 'foo', bar: 'bar' }),      url = this.href, xpath = "//p", env = 'store://datatables.org/alltableswithkeys',      yql = 'select * from htmlpost where url="' + url + '" and postdata="' + post + '"' +        ' and xpath="' + xpath + '"',        data = { q: yql, format: 'json', env: env, callback: 'yqlCallback' };    document.documentElement.firstChild.appendChild(      new Element('script', { type: 'text/javascript',        src: 'http://query.yahooapis.com/v1/public/yql?' + Object.toQueryString(data) +          '&r=' + Math.random()    }));  }  // 使用CSSHttpRequest  function loadUsingCHR(e) {    e.stop(); this.blur();    CSSHttpRequest.get(this.href, function(res) {      $('responses').insert('<p>' + res.escapeHTML() + '</p>');    });  }
0 0
原创粉丝点击