原生JS實現data方法

在開發中常常會在DOM上存儲一些自定義數據,咱們能夠經過setAttribute方法來實現。可是當數據爲引用類型時,存儲後的數據卻無效。這裏將用原生的JS對data方法進行實現。

使用setAttribute:

<div id="test-data"></div>
<p class="test-data-list"></p>
<p class="test-data-list"></p>
<p class="test-data-list"></p>
<p class="test-data-list"></p>
var testData = document.querySeletor('#test-data');
testData.setAttribute('baukh', {a:1,b:2})// 執行後DOM節點變化爲<div baukh="[object Object]"></div>
testData.getAttribute('baukh'); // => "[object Object]"

能夠從上面的代碼中看出,存進去的是個Object,取出來的是Object.toString()所產出的字符串。javascript

分析

在JS經典類庫-jQuery中存在data方法是經過jQuery.cache的方式進行數據存儲,那麼還有沒有其它方法能夠實現?html

因爲使用場景不一樣,我想實現的方式是將數據直接存儲到DOM節點上,以達到使用時更方便簡捷的目的。前端

那如何存儲? 變量testData存儲的是經過document.querySeletor('#test-data')獲取到的Element,而Element是Object的一個實例。經過[testData instanceof Object]能夠進行驗證。java

那麼一切都簡易了,即然是Object類型,那麼就能夠隨意的增刪自定義屬性。git

經過在Element的原型上增長data方法來實現DOM擴展

Element.prototype.data = function(key, value){
    var _this = this,
        _dataName = 'testData',  // 存儲至DOM上的對象標記, 這裏只是測試用名
        _data = {};
    // 未指定參數,返回所有
    if(typeof key === 'undefined' && typeof value === 'undefined'){
        return _this[_dataName];
    }
    // setter
    if(typeof(value) !== 'undefined'){
        // 存儲值類型爲字符或數字時, 使用attr執行
        var _type = typeof(value);
        if(_type === 'string' || _type === 'number'){
            _this.setAttribute(key, value);
        }
        _data = _this[_dataName] || {};
        _data[key] = value;
        _this[_dataName] = _data;
        return this;
    }
    // getter
    else{
        _data = _this[_dataName] || {};
        return _data[key] || _this.getAttribute(key);
    }
};

這裏來試一下:

var testData = document.querySelector('#test-data');
// 字符串類型測試
testData.data('name', 'baukh');
console.log(testData.data('name'));  // => 'baukh'
// 對象類型測試
testData.data('info', {'name': 'baukh', 'age': 27});
console.log(testData.data('info'));  // => Object {name: "baukh", age: 27}

解決NodeList存儲

如今還有一個問題, 經過Element.prototype綁定的方法只支持Element類生效,而對NodeList類並沒有效果.

能夠經過下面這些代碼進行效果測試:github

var testDataList = document.querySelectorAll('.test-data-list');  // 獲取的爲NodeList 而非 Element
testDataList.data('name', 'baukh');  // Uncaught TypeError: testDataList.data is not a function

這確定不是想要的結果, 那麼NodeList類就須要以下處理:

NodeList.prototype.data = function (key, value) {
    // setter
    if(typeof(value) !== 'undefined'){
        [].forEach.call(this, function (element, index) {
            element.data(key, value);
        });
        return this;
    }
    // getter
    else{
        return this[0].data(key, value); // getter 將返回第一個
    }
};

來測試下NodeList類的data實現:

var testDataList = document.querySelectorAll('.test-data-list');  // 獲取的爲NodeList 而非 Element
testDataList.data('name', 'baukh');  // Uncaught TypeError: testDataList.data is not a function
// 字符串類型測試
testDataList.data('name', 'baukh');
console.log(testDataList.data('name'));  // => 'baukh'
// 對象類型測試
testDataList.data('info', {'name': 'baukh', 'age': 27});
console.log(testDataList.data('info'));  // => Object {name: "baukh", age: 27}

這樣就功能上就完成了.
固然也能夠將NodeList與Element進行互換, 具體狀況具體考慮.
很簡單不是嗎?前端框架

順帶說一下,Array類型的數據,也能夠增長自定義屬性。

var ar = [1,2,3];
console.log(ar instanceof Object); //true 能添加自定義屬性的緣由就在這裏,Array也是Object的實例。
ar.test1 = {a:1,b:2};
console.log(ar);  //[1, 2, 3, test1: Object]
console.log(ar.test1); //Object {a: 1, b: 2}

隨筆一行
這是前端最好的時代, 這也是前端最壞的時代。 衆多前端框架滿天飛,隨着 jQuery 在前端行業的慢慢弱化,老是會有一種斯人遠去,何者慰籍的感受。互勉吧,各位。框架

另推薦個表格組件gridManager測試

相關文章
相關標籤/搜索