filter
(а ещё Safari - говорят, яблочные уроды пока не реализовали поддержку методов итераторов; впрочем, сделать обычный for...of
вместо reduce
не проблема):function countIntersections(data1, data2, key = n => n) {
const getKey = key instanceof Function ? key : n => n[key];
const keys = new Set(Array.from(data2, getKey));
return data1[Symbol.iterator]().reduce((acc, n) => acc + keys.has(getKey(n)), 0);
}
const result = countIntersections(newWord, glas);
.countIntersections(Array(7).keys(), Array(4).keys()) // 4
countIntersections('abCdE', 'ACe', n => n.toLowerCase()) // 3
countIntersections([ { id: 1 }, { id: 2 }, { id: 3 } ], [ { id: 3 } ], 'id') // 1
// такого у вас сейчас нет, сами догадайтесь, кому надо класс добавить
const containerSelector = '.container';
const buttonsSelector = `${containerSelector} .filter__navigation`;
const buttonSelector = `${buttonsSelector} [data-filter]`;
const buttonActiveClass = '_active';
const itemsSelector = `${containerSelector} .search__body`;
const itemSelector = `${itemsSelector} .filter-column`;
const itemHiddenClass = '_hide';
const itemFilterClassPrefix = 'filter__column_';
document.querySelectorAll(buttonSelector).forEach(n => {
n.addEventListener('click', onFilterButtonClick);
});
function onFilterButtonClick({ currentTarget: { dataset: { filter } } }) {
const activeItemClass = itemFilterClassPrefix + filter;
this.closest(buttonsSelector).querySelectorAll(buttonSelector).forEach(n => {
n.classList.toggle(buttonActiveClass, n === this);
});
this.closest(containerSelector).querySelectorAll(itemSelector).forEach(({ classList: cl }) => {
cl.toggle(itemHiddenClass, filter !== 'all' && !cl.contains(activeItemClass));
})
}
document.addEventListener('click', e => {
const button = e.target.closest(buttonSelector);
if (button) {
const { filter } = button.dataset;
const activeItemSelector = filter === 'all' ? '*' : `.${itemFilterClassPrefix}${filter}`;
for (const n of button.closest(buttonsSelector).children) {
n.classList.toggle(buttonActiveClass, n === button);
}
for (const n of button.closest(containerSelector).querySelector(itemsSelector).children) {
n.classList.toggle(itemHiddenClass, !n.matches(activeItemSelector));
}
}
});
{ 'старый ключ': 'новый ключ' }
:const keys = {
x: 'a',
y: 'b',
z: 'c',
};
const renameKeys = (obj, keys) =>
Object.fromEntries(Object
.entries(obj)
.map(([ k, v ]) => [ Object.hasOwn(keys, k) ? keys[k] : k, v ])
);
// или
const renameKeys = (obj, keys) =>
Object.keys(obj).reduce((acc, k) => (
acc[keys[k] ?? k] = obj[k],
acc
), {});
const newArr = arr.map(n => renameKeys(n, keys));
function renameKeys(keys, obj) {
for (const k in keys) {
if (obj.hasOwnProperty(k)) {
obj[keys[k]] = obj[k];
delete obj[k];
}
}
}
arr.forEach(renameKeys.bind(null, keys));
элемент.insertAdjacentHTML('afterend', разметка);
элемент.remove();
// или
элемент.outerHTML = разметка;
colors: markerColors, // Устанавливаем цвета маркеров в зависимости от результата
discrete
, позволяющим индивидуально настраивать внешний вид каждого маркера. Всплывающую подсказку это никак не затрагивает, так что её придётся кастомизировать отдельно. function bindRootContext(obj, context = obj) {
return new Proxy(obj, {
get(target, key) {
const val = target[key];
return (
val instanceof Function ? val.bind(Object.hasOwn(target, key) ? context : target) :
val instanceof Object ? bindRootContext(val, context) :
val
);
},
});
}
const obj = bindRootContext({
name: 'Root',
a: {
name: 'A',
logName() { console.log(this.name); },
b: {
name: 'B',
logName() { console.log(this.name); },
arr: [
{
name: '666',
logName() { console.log(this.name); },
},
function() { console.log(this.name); },
],
c: {
name: 'C',
logName() { console.log(this.name); },
},
},
},
});
obj.a.b.c.logName(); // Root
obj.a.b.logName(); // Root
obj.a.logName(); // Root
obj.a.b.arr[0].logName(); // Root
obj.a.b.arr[1](); // Root
кнопка.addEventListener('click', () => {
const [ li ] = шаблон.content.cloneNode(true).children;
li.querySelector('.card__image').src = инпут_с_ссылкой.value;
li.querySelector('.card__title').textContent = инпут_с_подписью.value;
список.insertAdjacentElement('afterbegin', li);
// или
список.prepend(document.importNode(шаблон.content, true));
список.querySelector('img').setAttribute('src', инпут_с_ссылкой.value);
список.querySelector('h2').innerText = инпут_с_подписью.value;
});
const columns = [ 'name', 'age', 'id' ];
.res.innerHTML = `
<table>
<thead>
<tr>${columns.map(n => `<th>${n}</th>`).join('')}</tr>
</thead>
<tbody>${arr[0]?.map((_, i) => `
<tr>${arr.map(n => `
<td>${n[i]}</td>`).join('')}
</tr>`).join('') ?? ''}
</tbody>
</table>`;
const table = document.createElement('table');
columns.forEach(function(n) {
this.append(document.createElement('th'));
this.lastChild.textContent = n;
}, table.createTHead().insertRow());
arr[0]?.forEach(function(_, i) {
const tr = this.insertRow();
arr.forEach(n => tr.insertCell().textContent = n[i]);
}, table.createTBody());
res.append(table);
// если гарантируется отсутствие thead и tfoot, или их содержимое также должно учитываться
const { rows } = table;
// если tbody один
const [ { rows } ] = table.tBodies;
// если tbody несколько
const rows = Array.prototype.flatMap.call(table.tBodies, n => [...n.rows]);
// или
const rows = [].concat(...Array.from(table.tBodies, n => [...n.children]));
// или
const rows = table.querySelectorAll('tbody tr');
const middle = arr => arr[arr.length >> 1];
// или
const middle = arr => arr[Math.floor(arr.length / 2)];
// или
const middle = arr => arr[Math.round(~-arr.length / 2)];
// или
const middle = arr => arr[(arr.length - arr.length % 2) / 2];
const cell = middle(middle(rows).cells);
// или
const cell = middle([].reduce.call(rows, (acc, n) => (acc.push(...n.children), acc), []));
// или, без получения строк
const cell = middle(table.querySelectorAll('tbody td'));
const el = document.querySelector('p');
const strings = [ 'hello, world!!', 'fuck the world', 'fuck everything' ];
const delay = 100;
function Typewriter(el, strings, delay) {
let i = 0;
let length = 0;
return setInterval(() => {
if (++length > strings[i].length) {
i = -~i % strings.length;
length = 0;
}
el.textContent = strings[i].slice(0, length);
}, delay);
}
const intervalId = Typewriter(el, strings, delay);
// хотим остановить, делаем так: clearInterval(intervalId);
function Typewriter(el, strings, delay) {
let timeoutId = null;
(function step(i, length) {
length = -~length % -~strings[i].length;
i = (i + !length) % strings.length;
el.innerText = strings[i].substring(0, length);
timeoutId = setTimeout(step, delay, i, length);
})(0, 0);
return () => clearTimeout(timeoutId);
}
const stop = Typewriter(el, strings, delay);
// хотим остановить, делаем так: stop();
При повторном нажатии на кнопку максимальным числом выскакивает число удалённого li.
remove
приведёт к исчезновению элемента из lis
? Искать максимальный элемент следует среди тех, что всё ещё присутствуют на странице, а не тех, что были изначально - то есть, или надо получать элементы непосредственно перед поиском, или коллекция элементов должна быть динамической.function max(data, key = n => n) {
const getVal = key instanceof Function ? key : n => n[key];
let result = null;
for (const n of data) {
const val = getVal(n);
if (!result || result[1] < val) {
result = [ n, val ];
}
}
return result?.[0];
}
document.querySelector('button').addEventListener('click', function(e) {
max(this, n => +n.textContent)?.remove();
e.target.disabled = !this.length;
}.bind(document.querySelector('ul').children));
getMaxQuestionsLeft(question, depth) {
return Math.max(
depth,
...question.nextQuestions.map(n => {
return this.getMaxQuestionsLeft(this.questions[n], depth + 1);
})
);
},
get progress() {
const ac = this.answersCount;
return Math.floor(ac / (ac + this.getMaxQuestionsLeft(this.question, 0)) * 100);
},
get maxQuestionsLeft() {
let result = 0;
for (const stack = [ [ this.question, 0 ] ]; stack.length;) {
const [ q, d ] = stack.pop();
result = Math.max(result, d);
stack.push(...q.nextQuestions.map(n => [ this.questions[n], -~d ]));
}
return result;
},
get progress() {
const { answersCount: ac } = this;
return ac / (ac + this.maxQuestionsLeft) * 100 | 0;
},
key
при переборе p
for...in
undefined
(ну, так язык устроен). Т.е., массива строк у вас нет. Массив строк - это когда всё его содержимое является строками. Откуда в массиве взялись undefined
(да, у вас их там несколько)? Цикл for...in
обрабатывает не только собственные свойства объектов, но и те, что доступны через цепочку прототипов. Значения которых уже не являются элементами и, соответственно, не имеют запрашиваемого вами свойства textContent
. Раз свойства нет, получаете undefined
.const getText = el => el.textContent;
), чтобы туда не попадал всякий мусор - лучше бы вообще отказаться от применения for...in
, варианты разные есть:const list = Array.from(p, getText);
// или
const list = Array.prototype.map.call(p, getText);
// или
const list = [];
for (const n of p) {
list.push(getText(n));
}
// или
const list = [];
for (let i = 0; i < p.length; i++) {
list[i] = getText(p[i]);
}
// или
const list = [];
p.forEach((n, i) => list.splice(i, 0, getText(n)));
for...in
, то прежде чем что-то делать с конкретным свойством, убедитесь, что оно является собственным, методы Object.hasOwn
и Object.prototype.hasOwnProperty
вам в этом помогут. const getWeekdaysOfMonth = (year, month) =>
Array.from(
{ length: new Date(year, month, 0).getDate() },
function() {
this[0].setDate(-~this[0].getDate());
this[1] += this[0].getDay() === 1 || !this[1];
return `неделя ${this[1]}, ` + this[0].toLocaleString('ru-RU', {
day: 'numeric',
weekday: 'short',
});
},
[ new Date(year, ~-month, 0), 0 ]
);
const may2024 = getWeekdaysOfMonth(2024, 5);
const sep2023 = getWeekdaysOfMonth(2024, -3);
const jun2021 = getWeekdaysOfMonth(2020, 18);
.then( (x) => x + 1, (x) => x + 3 ) //promise rejected, value = 14
.then((x) => x * 20)
. every
для пустого массива?||
? const months = Object.fromEntries(Array.from(
{ length: 12 },
(_, i) => [ new Date(0, i).toLocaleString('ru-RU', { month: 'long' }), i ]
));
function parseDate(str) {
const [ , month, day, year, hour, minute ] = str.match(/(\S+) (\d+), (\d+) (\d+):(\d+)/);
return +new Date(year, months[month.toLowerCase()], day, hour, minute);
}