// такого у вас сейчас нет, сами догадайтесь, кому надо класс добавить
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));
el.insertAdjacentHTML('afterend', html);
el.remove();
// или
el.outerHTML = html;
// или
el.replaceWith(...new DOMParser()
.parseFromString(html, 'text/html')
.body
.children
);
// или
el.replaceWith(
(t => (t.innerHTML = html, t.content))
(document.createElement('template'))
);
const arr = str.split('&').map(n => (
n = n.split('='),
n[1] = isNaN(n[1]) ? n[1] : Number(n[1]),
({
name: n[0].endsWith('[]')
? `${n[0].slice(0, -2)}${typeof n[1] === 'number' ? `_${n[1]}` : ''}`
: n[0],
value: n[1],
})
));
const arr = Array.from(
new URLSearchParams(str),
([ k, v ]) => ({
name: k.replace(/\[\]$/, ''),
value: Number.isNaN(+v) ? v : +v,
})
);
const arr = [...str.matchAll(/([^&]+)=([^&]*)/g)].map(([ , k, v ]) => ({
name: k.match(/(.*?)(\[\])?$/)[1],
value: /^-?\d+(\.\d+)?$/.test(v) ? parseFloat(v) : v,
}));
<el-table
ref="table"
...
>
...
const table = ref();
onMounted(() => {
new Sortable(table.value.$el.querySelector('thead tr'), {
handle: 'span',
onEnd(e) {
columns.value.splice(e.newIndex, 0, columns.value.splice(e.oldIndex, 1)[0]);
},
});
});
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
Можно ли пользоваться структурами данных из SDK при решении алгоритмических секций?
читинг или норм решение?
mergedList.sort()
кнопка.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;
});
for (const i of arr[0]?.keys() ?? []) {
res.innerHTML += i ? '<br>' : '';
for (const n of arr) {
res.innerHTML += `${n[i]} `;
}
}
const columns = [ 'name', 'age', 'id' ];
.res.insertAdjacentHTML('beforeend', `
<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);
data: () => ({
active: [ 0, -1 ],
...
methods: {
onKeyDown(e) {
const { items } = this;
const step = ({
ArrowUp: -1,
ArrowDown: 1,
})[e.key];
if (step && items.some(n => n.children?.length)) {
let iItem = this.active[0];
let iChild = this.active[1] + step;
while (!items[iItem].children?.[iChild]) {
iItem = (iItem + step + items.length) % items.length;
iChild = step === 1 ? 0 : ~-items[iItem].children?.length;
}
this.active = [ iItem, iChild ];
}
},
...
<ul>
<li v-for="(item, iItem) in items">
<ul>
<li
v-for="(child, iChild) in item.children"
:class="{ active: active[0] === iItem && active[1] === iChild }"
...
computed: {
children() {
return this.items.flatMap(n => n.children ?? []);
},
...
data: () => ({
active: null,
...
methods: {
onKeyDown({ key }) {
const { length } = this.children;
const step =
key === 'ArrowUp' ? -1 :
key === 'ArrowDown' ? 1 :
0;
if (step && length) {
const active = this.active ?? (step === 1 ? -1 : length);
this.active = Math.max(0, Math.min(length - 1, active + step));
}
},
...
<ul>
<li v-for="item in items">
<ul>
<li
v-for="child in item.children"
:class="{ active: children[active] === child }"
...
чтобы данные не терялись
const searchIndex = ref(-1);
function onKeydown({ key }) {
if (key === 'Escape') {
searchQuery.value = '';
return;
}
const data = filteredProducts.value ?? [];
const { length } = data;
if (length) {
const index = searchIndex.value;
const step = +(key === 'ArrowDown') || -(key === 'ArrowUp');
if (step) {
searchIndex.value = (Math.max(-1, index + step) + length) % length;
} else if (key === 'Enter' && index !== -1) {
onSelectProduct(data[index]);
}
}
}
watch(() => !!searchQuery.value.length, value => value
? document.addEventListener('keydown', onKeydown)
: document.removeEventListener('keydown', onKeydown)
);
v-for
, назначаем элементу списка класс, который его визуально выделит:.active {
background: #ccc;
}
<li
v-for="(n, i) in filteredProducts"
:key="n.id"
:class="{ active: i === searchIndex }"
@mouseenter="searchIndex = i"
@click="onSelectProduct(n)"
>
watch(filteredProducts, () => searchIndex.value = -1);
const filteredProducts = computed(() => {
const search = searchQuery.value.toLowerCase();
return search.length > 2
? data.value.filter(n => n.title.toLowerCase().includes(search))
: null;
});
<ul v-if="filteredProducts" class="search-result">
<li v-if="!filteredProducts.length">
<h3>ничего не найдено</h3>
</li>
<li v-for="n in filteredProducts">
<div>
<Image :src="n.image" :alt="n.title" width="40" height="40" />
</div>
<h3>{{ n.title }}</h3>
<div>
<p>{{ n.price }}</p>
</div>
</li>
</ul>
v-for
.