出於交互考慮,selection支持監聽(listening)和分派(dispatching)事件。javascript
根據typenames向元素添加或刪除事件監聽,類型爲字符串如click、mouseover、DOM event type都支持,能夠經過click.foo添加回調函數,多個name用空格分隔。
當一個事件分派到元素上,lisetener老是會看到最後的數據,若是以前已經綁定了事件會更新事件,傳入null刪除事件,.foo刪除全部監聽。
option設置監聽器的特性,capturing仍是passive,詳情查看addEventListener
未指定監聽器時會返回監聽器(第一個)。
源碼實現中給selection添加了一個__on的數組對象存儲事件,並區分了事件類型進行調用filterContextListener/contextListener,首先解析typenames,而後根據參數去判斷添加刪除html
export default function (typename,value,capture) {
var typenames = parseTypenames(typename + ""),i,n=typenames.length,t;//解析typename字符串
if(arguments.length < 2){//一個參數返回,查找對應的事件的value
var on=this.node().__on;
if(on) for(var j=0,m=on.length,o;j<m;++j){
for(i=0,o=on[j];i<n;++i){
if((t=typenames[i]).type === o.type && t.name === o.name){
return o.value;
}
}
}
return ;
}
//2個及以上參數的狀況
on = value?onAdd : onRemove;//這裏判斷value爲null時刪除不然添加事件
if(capture == null) capture =false;
for(i=0;i<n;i++) this.each(on(typenames[i],value,capture));//這裏對每一個typename查找一次,效率比較低,n*n
return this;
}
複製代碼
按順序將指定類型的自定義事件發送到元素,parameters有這幾個選項,bubbles,反向樹的順序即冒泡事件,cancelable,event.preventDefault阻止事件傳播,detail,關聯自定義數據,若是是個函數計算後返回上述選項。
代碼實現上使用CustomEvent、createEvent、initEvent建立自定義事件。java
function dispatchEvent(node,type,params) {
var window = defaultView(node),
event=window.CustomEvent;
if(typeof event === "function"){
event = new event(type,params);//>>>
}else{
event = window.document.createEvent("Event");
if(params) event.initEvent(type,params.bubbles,params.cancelable),event.detail=params.detail;
else event.initEvent(type,false,false);
}
node.dispatchEvent(event);
}
複製代碼
保存當前的事件(存在時),這是在調用監聽器時設置並在終止後重置,使用此選項能夠訪問標準事件字段如 event.timeStamp event.preventDefault,雖然可使用event.pageX,event.pageY,但使用d3.mouse,d3.touch,d3.touches,轉化爲本地座標更容易。node
使用that做爲this傳入arguments參數調用event事件,在調用期間,d3.event設置爲指定事件,函數返回後回調,此外,將event.sourceEvent設爲先前的d3.event,容許用戶自定義事件對原生事件的引用,返回listener返回的值。
源碼d3.event就是存儲在on.js中的一個變量,回調的這種方式不會影響原來的值;git
export function customEvent(event1,listener,that,args) {
var event0=event;
event1.sourceEvent=event;//sourceEvent做者定義的事件層級,返回最頂端的事件
event=event1;
try{
return listener.apply(that,args);
}finally {
event=event0;
}
}
複製代碼
返回當前事件對應容器(HTML或SVG元素)的[x,y]座標,點擊後的座標位置轉換方法以下(point.js)github
export default function (node, event) {
var svg=node.ownerSVGElement || node;
if(svg.createSVGPoint){
var point=svg.createSVGPoint();
point.x =event.clientX, point.y=event.clientY;
point = point.matrixTransform(node.getScreenCTM().inverse());
return [point.x,point.y];
}
var rect=node.getBoundingClientRect();
return [event.clientX - rect.left - node.clientLeft,event.clientY-rect.top-node.clientTop];
}
複製代碼
返回指定識別碼identifier容器下的觸摸座標[x,y],若是在指定identifier下touch事件,返回null,這對於忽略在觸摸移動實踐中只有部分觸摸移動的狀況很是有效,默認爲changeTouches。
代碼實現中利用changeTouches,去尋找指定的事件點發生的位置。web
以雙元數組形式返回觸摸事件點[[x1, y1], [x2, y2], …]。segmentfault
返回指定事件相對於容器的座標[x,y],如上面(point.js)數組
對於高級用法,selections提供了用戶自定義控制流的方法。bash
相似於數組的each,爲每一個元素調用function,代碼實現固然爲selection本身的數據結構而配置的。對於建立同時訪問父元素和子元素上下文很是有用。
parent.each(function(p, j) {
d3.select(this)
.selectAll(".child")
.text(d => `child ${d.name} of ${p.name}`);
});
複製代碼
稍微分析一下,this是parent的上下文,咱們再去選擇chiild此時text中的d的this就成了孩子節點的上下文,在此同時也是能夠訪問p.namem,就達到同時訪問的目的了。
調用指定函數一次,傳入參數返回selection,至關於手動調用函數,但這個方法支持鏈式調用,例以下面可重用的設置樣式的函數:
function name(selection, first, last) {
selection
.attr("first-name", first)
.attr("last-name", last);
}
複製代碼
而後調用:
d3.selectAll("div").call(name, "John", "Snow");
複製代碼
與下面結果相同:
name(d3.selectAll("div"), "John", "Snow");
複製代碼
惟一的區別是selection.call爲了支持鏈式調用會返回selection,而不是name的返回值。
若是selection不包含元素,返回true
返回選擇器中全部元素
返回選擇器第一個非空節點
返回選擇器中節點的數量
d3 local容許你定義獨立於數據的狀態,例如在渲染時間序列時,您可能須要使用相同的x比例尺以及不一樣的y比例尺
聲明一個新的局部變量
const foo = d3.local();
複製代碼
與var相同的是每個local是惟一的,不一樣的是local的範圍由DOM肯定。
給該local設置值,並返回value:
selection.each(function(d) { foo.set(this, d.value); });
複製代碼
若是隻添加了一個變量,也能夠這樣:
selection.property(foo, d => d.value);
複製代碼
從這就能夠看出,咱們能夠給元素添加多個local。
返回節點上的local值,若是未定義local返回最近祖先上的值,沒有返回undefined。
刪除該節點上的local值,若是刪除以前定義了local則返回true,該節點沒有local返回false,祖先上的local不受影響,所以local.get仍返回值。
返回local自動生成標識符,相似於一個識別碼用來惟一之別nodelocal上的value的值,換能夠經過element[local](這裏是由於local也是個對象,傳入時會自動去調用toString)或者selection.property來設置或獲取local的value。 源代碼:
var nextId=0;
export default function local() {
return new Local;
}
function Local() {
this._="@"+(++nextId).toString(36);
//Number.prototype.toString([radix])中的
// radix 指定要用於數字到字符串的轉換的基數(從2到36)。
// 若是未指定 radix 參數,則默認值爲 10。全部不在範圍內的基數會報錯
}
Local.prototype=local.prototype={
constructor:Local,
get:function (node) {
var id=this._;
while(!(id in node)) if (!(node=node.parentNode)) return;
return node[id];
},
set:function (node,value) {
return node[this._]=value;
},
remove:function (node) {
return this._ in node && delete node[this._];
},
toString: function () {
return this._;
}
}
複製代碼
總之local方便用戶在局部範圍進行操做,這個範圍指元素節點,實現原理就是給節點添加屬性,爲了名字不重複定義了一個自動生成的標識符this._來取得惟一的名字,並把他們統一到一個對象Local中。
selection的事件處理,最核心的是on方法,靈活多變的事件添加,其次就是用戶自定義事件的customEvent和觸發dispatch,對於在svg畫圖的狀況下,咱們很是有必有知道的是觸發事件相對於svg下的座標,mouse、touch、touches、clienPoint解決了這個問題。 對於控制流中的函數,是做者在完成整個selection模塊時產生的一些中間組件,做者把他們抽象出來,可讓用戶在使用時更加便利。
對於局部變量部分,local的定義就是一個對selection局部進行操做的變量。
到這裏就把selection模塊的內容分4篇文章說完了,其中最主要的仍是數據綁定部分,這也是d3數據驅動的最大的特點,算是d3js的核心了。而後目前爲止完成了3個模塊的源碼解析,任重而道遠啊!
源碼及解析:github.com/dongoa/evs-…
MouseEvent.relatedTarget事件屬性:developer.mozilla.org/en-US/docs/…
timeStamp 事件屬性:www.w3cschool.cn/jsref/event…
CustomEvent自定義事件:www.jianshu.com/p/1cf1c80c0…
changedTouches:www.cnblogs.com/mengff/p/60…
SVGElement:developer.mozilla.org/zh-CN/docs/…
SVG窗口座標系的轉換:blog.iderzheng.com/something-a…
offsetLeft,Left,clientLeft的區別:www.cnblogs.com/panjun-Done…
changedTouches:segmentfault.com/q/101000000…
TouchEvent:developer.apple.com/documentati…