Укороченный querySelector - вызовов функции цепочкой
Хочу написать аналог document.querySelector('selector'), но в одну букву - q('selector'). Реализация выглядит просто:
var q = props => document.querySelector(props)
Но querySelector можно вызывать в цепочке: element.querySelector('sel1').querySelector('sel2'), а сокращенную функцию так вызывать нельзя. Хочется получить q('sel1').q('sel2').q('sel3')
Похожая проблема рассматривалась тут, но там нужно обязательно добавить вызывать value()
Добавление: Обычно укороченную функцию очень удобно использовать, но если объект определен ранее, неплохо было бы сделать "цепочный" вызов от объекта. Как я теперь понял, достаточно, чтобы объектом был только первый элемент, т.е. достаточно вызывать вызывать функцию, у которой "может не быть" первого аргумента. Главное, задать правильный вопрос!
var q = (prop, props = prop) => (prop.length ? document : prop).querySelector(props);
Ответы (3 шт):
Написал даже красивее: q('sel1', 'sel2', 'sel3'), где селекторы ищутся по цепочке. И запись не сильно длиннее получилась:
var q = (...props) => props.reduce((el, prop) => el.querySelector(prop), document)
Немного докрутил, чтобы в качестве первого аргумента можно было использовать объект
var q = (...props) => props.reduce((el, prop) => el.querySelector(prop), props[0].length ? document : props.shift());
Финальная правка ответа. Я понял, что мне нужен только первый элемент, поэтому меня устроит небольшое расширение изначального варианта - q(elem, 'selectors'):
var q = (prop, props = prop) => (prop.length ? document : prop).querySelector(props);
Наконец-то понял что вы хотели, вот реализация :)
class MultiElement {
constructor(selector, context) {
if(context === null) {
throw new Error('Context is null!')
}
this.el = (context || document).querySelector(selector)
}
q(selector) {
return new MultiElement(selector, this.el)
}
}
function q(selector) {
return new MultiElement(selector)
}
// Пример использования
let element = q('.el1').q('.el2').el
console.log(element.className)
<div class="el2 uncorrect_el2"></div>
<div class="el1">
<div class="el2">
<div class="el3">
<div class="el4"></div>
</div>
</div>
</div>
Внимание!
Такой способ не является оптимальным или быстрым, намного проще и быстрее будет использовать CSS селекторы. Вот пример работы с ними:
let element = document.querySelector('.el1 > .el2')
console.log(element.className)
<div class="el1">
<div class="el2">
<div class="el3">
<div class="el4"></div>
</div>
</div>
</div>
Возможно не лучший вариант, но я все же вкину его сюда, т.к способ все же рабочий и удовлетворяет условиям.
В функции q с помощью querySelector находим нужный элемент. Если этот элемент найден, то добавляем ему метод q и возвращаем этот самый пропатченный элемент.
/**
* @returns {Element | null}
* у Element нет метода Element.q, дальнейший чейнинг с такой реализацией невозможен
*/
// var q = (props) => document.querySelector(props);
/**
* @param {string} - selector
* @returns {Element | null}
* Не использовал стрелочную функцию, т.к она не имеет this и берет его извне
*/
function q(selector) {
const element = this instanceof Window
? document.querySelector(selector)
: this.querySelector(selector);
element.q = q;
return element;
}
console.log(q('sel1').q('sel2').q('sel3')) // sel3 flag
console.log(q('sel3')) // sel3 wrong
console.log(q('sel4').q('sel5')); // TypeError
<sel3>wrong</sel3>
<sel1>
<sel2>
<sel3>flag</sel3>
</sel2>
</sel1>
Это, наверное, наименее замороченный способ.