attributes: [], // наполняется посредством rest API
props: [ 'value' ]
; а при необходимости его обновить отдаёт наверх новый массив: this.$emit('input', this.value.map(...))
. Это позволит использовать на компоненте директиву v-model
: <attributes-list v-model="attributes" />
.document.getElementById(selector).checked = true;
$emit
:methods: {
update(attr, checked) {
this.$emit('input', this.value.map(n => n === attr
? { ...n, checked }
: n
));
},
},
<div v-for="n in value">
<input type="checkbox" :checked="n.checked" @input="update(n, $event.target.checked)">
<button @click="update(n, true)">Да</button>
<button @click="update(n, false)">Нет</button>
</div>
v-model
на чекбоксах, то можно собрать объект вида { 'имя/id/ещё что-то уникальное': checked }
и завернуть его в Proxy:computed: {
attributes() {
return new Proxy(Object.fromEntries(this.value.map(n => [ n.name, n.checked ])), {
set: (target, key, val) => {
this.$emit('input', this.value.map(n => n.name === key
? { ...n, checked: val }
: n
));
return true;
},
});
},
},
<div v-for="(v, k) in attributes">
<input type="checkbox" v-model="attributes[k]">
<button @click="attributes[k] = true">Да</button>
<button @click="attributes[k] = false">Нет</button>
</div>
document.querySelector('table').addEventListener('click', e => {
const td = e.target.closest('td');
if (td) {
// поскольку речь идёт о ячейках таблицы, то нет причин использовать что-то иное
const index = td.cellIndex;
// но вообще, можно и метод массива припахать
const index = Array.prototype.indexOf.call(td.parentNode.children, td);
// или вручную посчитать, как далеко удастся уйти по ссылкам на предыдущие элементы
let index = 0;
for (let el = td; el = el.previousElementSibling; index++) ;
}
});
<div @click="changeCategory(subcat)">
...
<div v-for="(v, k) in active">{{ k }}: {{ v }}</div>
changeCategory({ a, b }) {
this.active = { a, b };
},
Как сделать, чтобы при загрузке сайта первый пункт первого меню уже был выбранным?
created() {
this.changeCategory(this.categories[0].sub[0]);
},
v-if
/v-else
- один для показа форматированного значения, другой для редактирования:<input v-if="edit" v-model="price" @blur="edit = false">
<input v-else :value="formatPrice(price)" @focus="edit = true">
data: () => ({
edit: false,
price: 666,
}),
methods: {
formatPrice: price => `${(+price).toLocaleString()} руб.`,
},
get searchtest() {
const search = this.state.search.toLowerCase().trim();
return search
? searchtest.filter(n => Object.values(n).some(m => m.toLowerCase().includes(search)))
: searchtest;
}
{this.searchtest.map((kef, i, a) => ( <div style={style}> <div key={kef.name} data-id={kef.name}> name: {kef.name} </div> <div key={kef.id} data-id={kef.id}> id: {kef.id} </div> </div> ))}
{this.searchtest.map(n => (
<div key={n.id}>
<div>name: {n.name}</div>
<div>id: {n.id}</div>
</div>
))}
.gallery__item:hover::before {
content: "";
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 20px);
left: 10px;
top: 10px;
box-sizing: border-box;
border: 2px solid #ccc;
background: transparent;
z-index: 666;
}
\b[A-Z][a-z]?\d?\b
const arr = str.split('-');
const end = 'final';
const obj = arr.reduceRight((acc, n) => ({ [n]: acc }), end);
const obj = (function createObj(i) {
return i < arr.length
? { [arr[i]]: createObj(i + 1) }
: end;
})(0);
const result = graphData.map((n, i) => ({ ...n, color: palette[i % palette.length] }));
const combine = (arr, keys, values) =>
arr.map(({ ...n }, i) => (
values.forEach((m, j) => n[keys[j]] = m[i % m.length]),
n
));
const result = combine(graphData, [ 'color' ], [ palette ]);
// при клике по кнопке определяем её индекс и хватаем .photos__like-count с таким же индексом
document.addEventListener('click', ({ target: t }) => {
if (t.classList.contains('photos__like-icon')) {
const index = [...document.querySelectorAll('.photos__like-icon')].indexOf(t);
const counter = document.querySelectorAll('.photos__like-count')[index];
const count = counter.textContent;
counter.textContent = t.classList.toggle('active') ? -~count : ~-count;
}
});
// но если у каждой пары icon-count если отдельный общий предок, то можно и попроще сделать
document.addEventListener('click', ({ target: t }) => {
if (t.matches('.photos__like-icon')) {
t
.closest('селектор общего предка кнопки и элемента с количеством')
.querySelector('.photos__like-count')
.textContent -= t.classList.toggle('active') ? -1 : 1;
}
});
const toggleLike = function({ target: t }) {
this[t.dataset.index].innerText -= [ 1, -1 ][+t.classList.toggle('active')];
}.bind(document.querySelectorAll('.photos__like-count'));
document.querySelectorAll('.photos__like-icon').forEach((n, i) => {
n.dataset.index = i;
n.addEventListener('click', toggleLike);
});
// или, при наличии отдельных общих предков у каждой пары .photos__like-icon и .photos__like-count
const toggleLike = ({ target: t }) => t
.closest('селектор общего предка кнопки и элемента с количеством')
.querySelector('.photos__like-count')
.innerText -= t.classList.toggle('active') ? -1 : 1;
for (const n of document.querySelectorAll('.photos__like-icon')) {
n.addEventListener('click', toggleLike);
}
const Menu = ({ items }) =>
items instanceof Array && items.length
? <ul>
{items.map(n => (
<li key={n.id}>
<a>{n.title}</a>
<Menu items={n.children} />
</li>
))}
</ul>
: null;