簡體   English   中英

Javascript:鏈接jQuery之類的元素

[英]Javascript: Chaining on elements like jQuery

我正在嘗試在一定程度上復制jQuery的元素操作。 現在,我發現非常有用的是.first()選擇器。 我希望能夠鏈接這樣的功能;
getElement(selector).first().hasClass(className);

現在,我得到了2個問題(請注意,我的代碼示例已被最小化,所以請不要對錯誤處理發表任何評論。)

var getElement = function(selector, parent) {
  ret           = document.querySelectorAll(selector);
  this.element  = ret;
  this.hasClass = function(className) {
    className.replace('.', '');
    if(this.multiple())
    {
      console.log('Cannot use hasClass function on multiple elements');
      return false;
    }
  };

  this.first = function() {
    this.element = this.element[0];
    return this;
  };
  return this;
};

我目前的問題

如果我調用我的函數;

var $test = getElement('.something'); //result: nodelist with .something divs

如果我要求結果中的第一個元素;

$test.first(); //Result: First div, perfect!

但是,現在,如果我再次調用$test ,它將用first()的結果替換elements屬性,這意味着我“丟失”了我的舊值。 我不想丟失它們,我只想要特定功能的first()函數。 然后我要$test再次返回所有元素。 此外,回顧first()現在將結束undefined ,因為只有1內左元素this ,因為它已經從對象中刪除舊的元素。

另一嘗試

現在,我還嘗試通過返回第一個孩子而不是整個類對象來解決這個問題。

this.first = function() {
  return this.element[0];
};

但是,我將$test.first().hasClass(className); //Returns ERROR, method hasClass undefined $test.first().hasClass(className); //Returns ERROR, method hasClass undefined

這是因為.hasClass存在於原始this ,因為我現在返回該元素,所以不再返回它。

我試圖從jQuery的庫中獲取一些東西,盡管那讓我更加困惑。

我已經搜索了這個主題,但是使用我發現的所有“鏈接方法”解決方案,它們似乎都覆蓋了對象的原始值,這不是我想要的。 另一個解決方案實際上要求我一遍又一遍地重新啟動該對象,這對我來說似乎不是很有效。感謝您的幫助。 我以為我將完全以錯誤的方式進行此操作。

-如果您可以幫助我,請說明您的解決方案為何有效。 我真的覺得,如果我理解了這一點,我對javascript的理解將會進一步擴展。 我只需要解決這個結構性(?)問題。

您外部函數中的this指的是窗口/全局對象。

而是返回ret變量本身。

內部函數(這些函數成為對象的方法)中, this按照您期望的方式運行。

這是一個替代解決方案,即使調用第first方法,該方法也允許鏈接:

 var getElement = function(selector, parent) { var ret = typeof selector == 'string' ? document.querySelectorAll(selector) : selector; ret.hasClass = function(className) { if(!this.classList) { console.log('Cannot use hasClass function on multiple elements'); return false; } else { return this.classList.contains(className); } }; ret.first = function() { return new getElement(this[0]); }; return ret; }; console.log(getElement('p').length); //2 console.log(getElement('p').first().innerHTML); //abc console.log(getElement('p').first().hasClass('test')); //true console.log(getElement('p').first().hasClass('nope')); //fase console.log(getElement('p').hasClass('test')); //false (multiple elements) 
 <p class="test"> abc </p> <p> def </p> 

類似的方法first()不能修改this ,應該創建一個新的對象,並返回。 您只能使用return this; 在修改元素而不是返回從元素派生的信息的方法中。

this.first = function() {
    return new getElement(this.element[0]);
};

請注意,您必須使用new getElement來創建對象,而不僅僅是getElement

這也需要更改構造函數,因此它可以接受選擇器字符串或元素:

var getElement = function(selector, parent) {
    var ret = typeof selector == "string" ? document.querySelectorAll(selector) : [selector];
    ...
}

您還應該考慮通過將方法放在原型中而不是在每個對象中定義方法,以適當的OO方式進行操作。

var getElement = function(selector, parent) {
  var ret = typeof selector == "string" ? document.querySelectorAll(selector) : [selector];
  this.element = ret;
};

getElement.prototype.hasClass = function(className) {
  className.replace('.', '');
  if (this.multiple()) {
    console.log('Cannot use hasClass function on multiple elements');
    return false;
  }
};

getElement.prototype.first = function() {
  return new getElement(this.element[0])
};

我認為最簡單的方法是返回一個包含所選節點的新類。 那將是最簡單的解決方案,因為您實際上不想突變任何先前的選擇器。

我舉了一個小例子,使用一些ES6,它使某些事情變得更容易使用,並且還帶有$來啟動所做的選擇。

您會注意到,首先進行的任何選擇都只是調用本機document.querySelectorAll但返回一個新的Node類。 firstlast方法也都返回這些元素。

最后, hasClass應該在當前節點選擇中的所有元素上起作用,因此它將迭代當前節點,並檢查其中的所有類, hasClass返回一個簡單的布爾值,因此您無法繼續在那里進行方法鏈接。

您希望鏈接的任何方法都應:

  • 返回this對象(當前節點)
  • 返回this對象的元素作為新節點,以便可以在此處進行任何進一步的操作

 const $ = (function(global) { class Node extends Array { constructor( ...nodes ) { super(); nodes.forEach( (node, key) => { this[key] = node; }); this.length = nodes.length; } first() { return new Node( this[0] ); } last() { return new Node( this[this.length-1] ); } hasClass( ...classes ) { const set = classes.reduce( (current, cls) => { current[cls] = true; return current; }, {} ); for (let el of this) { for (let cls of el.classList) { if (set[cls]) { return true; } } } return false; } } global.$ = function( selector ) { return new Node( ...document.querySelectorAll( selector ) ); }; return global.$; }(window)); let selector = $('.foo'); let first = selector.first(); // expect 1 console.log(first[0].innerHTML); let last = selector.last(); console.log(last[0].innerHTML); // expect 4 console.log( first.hasClass('foo') ); // expect true console.log( first.hasClass('bar') ); // expect false console.log( selector.hasClass('foo') ); // expect true console.log( selector.hasClass('bar') ); // expect true 
 <div class="foo">1</div> <div class="foo">2</div> <div class="foo bar">3</div> <div class="foo">4</div> 

這是我將如何處理的方法:

  1. 創建一個構造函數,例如Search ,其任務是根據輸入查找元素。 使用構造函數是正確的OO編程,並且您還具有在原型中定義一次方法的優點,並且所有實例都可以訪問它們。
  2. 確保上下文( this )是具有數字屬性和長度的類似數組的對象,以便您可以輕松地以傳統方式遍歷每個匹配的元素(使用for循環, [].forEach等)
  3. 創建一個名為getElement的函數,該函數將使用構造函數並返回結果,而不必始終使用new關鍵字。 由於該函數返回構造函數的實例,因此您可以像平時那樣鏈接所需的方法。
  4. 該方法first使用構造函數來創建新實例,而不是修改原始實例,因為其作用是返回第一個元素,而不是刪除除第一個元素以外的所有內容。
  5. 每次您想讓對象擁有一個新方法時,只需將其添加到構造函數的原型中即可。

片段:

 ;(function () { function Search (value) { var elements = []; /* Check whether the value is a string or an HTML element. */ if (typeof value == "string") { /* Save the selector to the context and use it to get the elements. */ this.selector = value; elements = document.querySelectorAll(value); } else if (value instanceof Element) elements.push(value); /* Give a length to the context. */ this.length = elements.length; /* Iterate over every element and inject it to the context. */ for (var i = 0, l = this.length; i < l; i++) this[i] = elements[i]; } /* The method that returns the first element in a Search instance. */ Object.defineProperty(Search.prototype, "first", { value: function () { return new Search(this[0]); } }); /* The global function that uses the Search constructor to fetch the elements. */ window.getElement = (value) => new Search(value); /* Create a reference to the prototype of the constructor for easy access. */ window.getElement.fn = Search.prototype; })(); /* Get all elements matching the class, the first one, and the first's plain form. */ console.log(getElement(".cls1")); console.log(getElement(".cls1").first()); console.log(getElement(".cls1").first()[0]); 
 /* ----- CSS ----- */ .as-console-wrapper { max-height: 100%!important; } 
 <!----- HTML -----> <div id = "a1" class = "cls1"></div> <div id = "a2" class = "cls1"></div> <div id = "a3" class = "cls1"></div> 


例:

在此示例中,我將一個名為hasClass的新方法添加到構造函數的原型中。

 /* The method that returns whether the first element has a given class. */ Object.defineProperty(getElement.fn, "hasClass", { value: function (value) { return this[0].classList.contains(value); } }); /* Check whether the first element has the 'cls2' class. */ console.log(getElement(".cls1").first().hasClass("cls2")); 
 <!----- HTML -----> <script src="//pastebin.com/raw/e0TM5aYC"></script> <div id = "a1" class = "cls1 cls2"></div> <div id = "a2" class = "cls1"></div> <div id = "a3" class = "cls1"></div> 

您可以更新getElement以便在向其發送元素時再次返回。

 var getElement = function(selector, parent) { var ret = null if (typeof selector === "string") { ret = document.querySelectorAll(selector); } else { ret = selector } this.element = ret; this.hasClass = function(className) { className.replace('.', ''); if (this.multiple()) { console.log('Cannot use hasClass function on multiple elements'); return false; } }; this.first = function() { this.element = getElement(this.element[0]); return this; }; return this; }; var test = getElement(".foo"); console.log(test.first()) console.log(test.first().hasClass) 
 <div class="foo">1</div> <div class="foo">2</div> <div class="foo">3</div> <div class="foo">4</div> 

您可以使用.querySelectorAll() ,spread元素和Array.prototype.find() ,后者返回數組中的第一個匹配項或undefined匹配項

 const getElement = (selector = "", {prop = "", value = "", first = false} = {}) => { const el = [...document.querySelectorAll(selector)]; if (first) return el.find(({[prop]:match}) => match && match === value) else return el; }; let first = getElement("span", {prop: "className", value: "abc", first: true}); console.log(first); let last = getElement("span"); console.log(all); 
 <span class="abc">123</span> <span class="abc">456</span> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM