Как сделать jQuery подобной систему выбора и действия (JavaScript)? - PullRequest
1 голос
/ 26 февраля 2020

Это не для использования в моем проекте, Только для учебных целей .

В jQuery, Когда мы звоним $('h1'). он просто возвращает все элементы h1 из документа. Опять же, когда мы делаем какое-то действие с элементом, таким как $('h1').hide(), он просто скрывает все элементы (круто, а?)

Я хочу изучить эту похожую функциональность, например:

function app(elm){
  const x = (typeof elm !== 'object') ? document.querySelectorAll(elm) : elm

   return {
      hide : function(){
         x.forEach( target =>{
             target.style.display = 'none';
         });
      }
   }
}

Это простой код здесь. Так что, если я назову это как app('h1').hide();, он скроет все элементы h1 из документа. Но если я называю это как app('h1'), он возвращает объект, который я возвращаю, это нормально. Здесь мне нужны все элементы h1 из документа, такие как jQuery. Я имею в виду, что это должно работать так,

$('h1') === app('h1') //JQuery is equal to myCFunction (problem)
$('h1').hide === app('h1').hide() //jQuery is equal to myCFunction (solved)

[ПРИМЕЧАНИЕ] Вот статья, которая похожа на мой вопрос, но это не мой ответ на вопрос. Ссылка на статью

Ответы [ 3 ]

0 голосов
/ 26 февраля 2020

Я думаю, что $ ("h1") не возвращает выбранные элементы. Хранит выбранные элементы. Вместо этого у нас может быть новая функция (getElement) для получения элементов select. Надеюсь, этот код поможет.

var App = function() {
        var x ;
        this.app = function (elem) {
            x = document.querySelectorAll(elem);
            return this;
        }
        this.hide = function(){
            x.forEach(target => {
                target.style.display = 'none';
            });
            return;
        }
        this.getElement = function(){
            return x;
        }

    }
    var $ = new App();
    $.app("h1").hide();
    console.log($.app("h1").getElement());
0 голосов
/ 26 февраля 2020

У меня есть в основном рабочее решение, но вам все еще нужно решить одну небольшую, но раздражающую проблему (см. Предостережение 3). В основном это сделано, поэтому я все равно это здесь выложу.

Я думаю, это то, что вы ищете:

function app(selector) {    
    const retArr = document.querySelectorAll(selector); // The array to return

    // Add proxies for all prototype methods of all elements
    for (let e of retArr) {
        let methods = getProtoMethods(e);
        for (let mKey in methods) {
            // Skip if the proxy method already exists in retArr
            if (retArr[mKey] !== undefined) continue;

            // Otherwise set proxy method
            Object.defineProperty(retArr, mKey, {
                value: function(...args) {
                    // Loop through all elements in selection
                    retArr.forEach(el => {
                        // Call method if it exists 
                        if (el[mKey] !== undefined) el[mKey](...args);
                    });
                }
            });
        }
    }

    return retArr;

    // Gets all prototype methods for one object
    function getProtoMethods(obj) {
        let methods = {};
        // Loop through all prototype properties of obj and add all functions
        for (let pKey of Object.getOwnPropertyNames(Object.getPrototypeOf(obj))) {
            // Skip properties that aren't functions and constructor
            if (pKey !== "constructor" && typeof obj[pKey] === "function") {
                methods[pKey] = obj[pKey];
            }
        }
        return methods;
    }
}

Идея состоит в том, чтобы поместить все выбранные объекты в массив, затем определите дополнительные методы в массиве. Он должен иметь все имена методов выбранных объектов, но эти методы на самом деле являются прокси этих оригинальных методов. Когда вызывается один из этих прокси-методов, он вызывает оригинальный метод для всех (см. Предостережение 1) выбранных объектов в массиве. Но в противном случае возвращенный объект можно просто использовать как обычный массив (или, точнее, NodeList в данном случае).


Однако стоит упомянуть, что с этой конкретной реализацией есть несколько предостережений.

  1. Список созданных прокси-методов представляет собой объединение методов всех выбранных объектов, а не пересечение . Предположим, вы выбрали два элемента - A и B. A имеет метод doA(), а B имеет метод doB(). Тогда массив, возвращаемый app(), будет иметь как прокси-методы doA(), так и doB(). Однако, когда вы вызываете, например, doA(), будет вызван только A.doA(), потому что, очевидно, B не имеет метода doA().
  2. Если выбранные объекты не имеют одно и то же определение для того же имя метода, прокси-метод будет использовать свои индивидуальные определения. Это обычно желаемое поведение при полиморфизме, но все же это что-то, что нужно иметь в виду.
  3. Эта реализация не пересекает цепочку прототипов, что на самом деле является серьезной проблемой. Он смотрит только на прототипы выбранных элементов, но не на прототипы прототипов. Поэтому эта реализация не работает хорошо с любым наследованием. Я попытался заставить это работать, сделав getProtoMethods() рекурсивным, и он работает с обычными JS объектами, но выполнение этого с элементами DOM выдает странные ошибки (TypeError: Illegal Invocation) (см. здесь ) , Если вы можете как-то решить эту проблему, то это будет полностью рабочее решение.

Это рекурсивный код c:

// Recursively gets all nested prototype methods for one object
function getProtoMethods(obj) {
    let methods = {};
    // Loop through all prototype properties of obj and add all functions
    for (let pKey of Object.getOwnPropertyNames(Object.getPrototypeOf(obj))) {
        // Skip properties that aren't functions and constructor
        // obj[pKey] throws error when obj is already a prototype object
        if (pKey !== "constructor" && typeof obj[pKey] === "function") {
            methods[pKey] = obj[pKey];
        }
    }
    // If obj's prototype has its own prototype then recurse.
    if (Object.getPrototypeOf(Object.getPrototypeOf(obj)) == null) {
        return methods;
    } else {
        return {...methods, ...getProtoMethods(Object.getPrototypeOf(obj))};
    }
}

Извините, я не могу решить вашу проблему на 100%, но, надеюсь, это по крайней мере несколько полезно.

0 голосов
/ 26 февраля 2020

Вы можете вернуть x вместо пользовательского объекта, но перед возвратом вставьте функцию hide в прототип x объекта, например x.prototype.hide = function(){/*...*/}.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...