Ответ для быстрых фильтров при вызове через Fetch
maria.ats
16 ноября 2020 г.
Заменяю запрос для подсчета в быстрых фильтрах $ajax на его полный аналог с fetch
Однако результат в этом случае - полный HTML страницы, а не count.
return fetch('./', {
method:'POST',
body: data,
dataType: 'json',
ContentType: 'application/json'
})
.then(response => response.json())
.then((count) => {
console.log(count);
});
method:'POST',
body: data,
dataType: 'json',
ContentType: 'application/json'
})
.then(response => response.json())
.then((count) => {
console.log(count);
});
Однако результат в этом случае - полный HTML страницы, а не count.
HostDEV.pw
16 ноября 2020 г.
maria.ats,
вероятно каких-то параметров в запросе не хватает
вероятно каких-то параметров в запросе не хватает
HostDev.pw - модули для HostCMS, Telegram: @hostdev
maria.ats
16 ноября 2020 г.
Вот и пытаюсь понять каких. Для эксперимента заменяла в стандартном куске кода только $ajax на fetch - обычно замена работает.
function fastFilter(form)
{
this._timerId = false;
this._form = form;
this.filterChanged = function(obj) {
if (this._timerId)
{
clearTimeout(this._timerId);
}
var $this = this;
this._timerId = setTimeout(function() {
$this._loadJson(obj);
}, 1500);
return this;
}
this._loadJson = function(obj) {
var data = this._serializeObject();
return fetch('./', {
method:'POST',
body: data,
dataType: 'json',
ContentType: 'application/json'
})
.then(response => response.json())
.then((count) => {
console.log(count);
});
}
this._serializeObject = function () {
var o = {fast_filter: 1};
var a = this._form.serializeArray();
$.each(a, function () {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
console.log(o);
return o;
};
}
function fastFilter(form)
{
this._timerId = false;
this._form = form;
this.filterChanged = function(obj) {
if (this._timerId)
{
clearTimeout(this._timerId);
}
var $this = this;
this._timerId = setTimeout(function() {
$this._loadJson(obj);
}, 1500);
return this;
}
this._loadJson = function(obj) {
var data = this._serializeObject();
return fetch('./', {
method:'POST',
body: data,
dataType: 'json',
ContentType: 'application/json'
})
.then(response => response.json())
.then((count) => {
console.log(count);
});
}
this._serializeObject = function () {
var o = {fast_filter: 1};
var a = this._form.serializeArray();
$.each(a, function () {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
console.log(o);
return o;
};
}
maria.ats
23 февраля 2021 г.
Оставлю целиком класс для фильтров
export class Filter {
constructor(container) {
this.container = container;
this.togglerClass ='js-toggler';
this.form = this.container.querySelector('form');
// переменные для построения пути фильтра
this.path = this.form.action;
this.priceFrom = null;
this.priceTo = null;
this.priceFromOriginal = null;
this.priceToOriginal = null;
this.producerField = this.form.querySelector('select[name = producer_id]');
this.priceFromBox = this.form.querySelector('input[name = price_from]');
this.sortingSelect = this.form.sorting;
this.inputs = null;
this.producerOption = null;
this.sortingOption = null;
// переменные для открытия и закрытия, а также отметки подчиненных/управляющих инбоксов
this.toggler = null;
this.targetElement = null;
this.checkbox = null;
this.parentLabel = null;
this.childLabels = null;
this.parentInbox = null;
this.popup = null;
this.timerId = null; // таймер для быстрого фильтра
this.filterChosenBlock = document.querySelector('.js-filter-chosen');
this.filterChosen = null; // выбранный фильтр для удаления
this.text = 'Больше фильтров';
this.closeClass = 'js-close';
this.deleteFilter = this.deleteFilter.bind(this);
this.destroy = this.destroy.bind(this);
this.init();
this.setListeners();
}
init() {
this.inputs = this.form.querySelectorAll('[data-property]:not(div)');
// проверяем все инпуты и выставляем родительскому лэйблу состояния отмечен, неотмечен, неопределен
this.inputs
.forEach(input => {
if (input.classList.contains('check')) {
if (input.checked) {
const newElt = this.createElt(input, 'button', 'filters-chosen__item', input.nextElementSibling.innerHTML);
this.filterChosenBlock.prepend(newElt);
}
}
});
}
filterApply() {
if (this.producerField) {
this.producerOption = this.producerField.option.selected;
if (parseInt(this.producerOption.value)) this.path += `${this.producerOption.dataset.producer.toLowerCase()}/`;
}
let tag_name = null;
if (this.inputs) {
this.inputs.forEach(input => {
let type = input.type;
let value = null;
let setValue = false;
if (input.name !== void 0 && input.name.indexOf('_to') !== -1) return;
switch(type) {
case 'checkbox':
case'radio':
if (input.checked) {
value += input.value;
setValue = type != 'checkbox' ? true : input.name.indexOf('[]') !== -1;
}
break;
case 'option':
if (input.selected) {
value = input.value;
setValue = true;
}
break;
case 'text':
case 'hidden':
value = input.value;
setValue = true;
break;
}
if (value && input.dataset.property !== tag_name) {
tag_name = input.dataset.property.toLowerCase();
if (input.name !== void 0 && input.name.indexOf('_from') !== -1) {
this.path +='';
} else {
this.path += `${tag_name}/`;
}
}
if (setValue && value) {
if (input.name !== void 0 && input.name.indexOf('_from') !== -1) {
const value2 = input.closest('.filter__fieldset-inside').querySelector('input[name $= "_to"]').value;
this.path += `${tag_name}-${value}-${value2}/`;
} else {
this.path += input.dataset.value !== ''
? `${input.dataset.value.toString().toLowerCase()}/`
: `${value.toString().toLowerCase()}/`
}
}
});
}
if (this.priceFromBox) {
this.priceFrom = this.priceFromBox.value;
this.priceTo = this.form.querySelector('input[name = price_to]').value;
this.priceFromOriginal = this.form.querySelector('input[name = price_from_original]').value
this.priceToOriginal = this.form.querySelector('input[name = price_to_original]').value
if (this.priceFrom && this.priceTo
&& (this.priceFroOriginalm !== this.priceFrom || this.priceToOriginal !== this.pticeTo)) {
this.path += `price-${priceFrom}-${priceTo}/`;
}
}
if (this.sortingSelect) {
this.sortingOption = this.sortingSelect.options.selectedIndex;
if (this.sortingOption !== 0) {
const sortingOptionValue = this.sortingSelect.options[this.sortingOption].value;
this.path += `?sorting=${sortingOptionValue.toLowerCase()}`;
}
}
window.location.href = this.path;
}
setListeners() {
this.container.addEventListener('click', (evt) => {
this.toggler = evt.target.closest(`.${this.togglerClass}`);
if (this.toggler) {
evt.preventDefault();
evt.stopImmediatePropagation();
this.openHandler();
return;
}
this.checkbox = evt.target.closest(`.check`);
if (this.checkbox) {
this.__fastFilter(this.checkbox);
this.changeState(this.checkbox);
}
if (evt.target.classList.contains('filter__btn')) {
evt.preventDefault();
this.filterApply();
}
});
if (this.filterChosenBlock) {
this.filterChosenBlock.addEventListener('click', this.deleteFilter);
}
this.form.addEventListener('submit',(evt) => {
evt.preventDefault();
this.filterApply();
})
}
// определяем состояние родительского эл-та
determineState(elt) {
this.parentContainer = elt.closest('.filter__container--parent');
if (!this.parentContainer) return; // если нет родительского - возвращаемся
this.parentLabel = this.parentContainer.querySelector('.filter__label--parent'); // определяем управляющий лэйбел
this.parentInbox = this.parentLabel.querySelector('input'); // определяем управляющий инпут
this.getChildren(this.parentContainer); //получаем подчиненные инпуты
this.checkParentInput(); // отправляем на проверку для установления состояния
}
// получаем подсиненные инпуты
getChildren(elt) {
this.childLabels = elt.querySelector('div').querySelectorAll('.check');
}
// изменение состояния управляющего инпута при клике на дочерний или дочернего при щелчке на подчиненный
changeState(elt) {
this.parentContainer = elt.closest('.filter__container--parent'); // получаем ближайший управляющий контейнер
if (!this.parentContainer) return; //если его нет, завершаем цикл
this.parentLabel = this.parentContainer.querySelector('.filter__label'); // получаем управляющий лэйбел
this.parentInbox = this.parentLabel.querySelector('.check'); // получаем управляющий инпут
this.getChildren(this.parentContainer); // собираем дочерние инпуты
// если целевой элт является уравляющим, проставляем всем подчиненным ставим им то же состояние, что и родительским
if (this.parentInbox === elt) {
const labelState = this.parentInbox.checked;
[...this.childLabels].forEach(label => {
label.indeterminate = 0;
label.checked = labelState;
});
return;
}
// если это один из подчиненных эл-тов, перепроверяем состояние управляющий эл-т
this.determineState(elt);
}
// проверка управляющего инпута
checkParentInput() {
// считаем все отмеченные подчиненные инпуты
const checkedChildren = this.parentContainer.querySelector('div').querySelectorAll('.check:checked');
const checkedChildrenCount = checkedChildren.length;
// если все отмечены, отмечаем родительский
if (checkedChildrenCount === this.childLabels.length) {
this.parentInbox.indeterminate = 0;
this.parentInbox.checked = 'checked';
if (this.parentContainer.classList.contains('closed')) {
this.parentContainer.classList.toggle('closed');
}
return;
}
// если со всех снята отметка, снимаем отметку с родительского
if (checkedChildrenCount === 0) {
this.parentInbox.indeterminate = 0;
this.parentInbox.checked = 0;
return;
}
// если отмечены не все, ставим indeterminate
this.parentInbox.indeterminate = true;
if (this.parentContainer.classList.contains('closed')) {
this.parentContainer.classList.toggle('closed');
}
}
__fastFilter(obj) {
if (this.timerId) {
clearTimeout(this.timerId);
}
this.timerId = setTimeout(() => {this._loadJson(obj)}, 500);
}
_loadJson(obj) {
const data = this._serializeObject();
return fetch('./', {
method:'POST',
body: data,
dataType: 'json',
headers: {
"Accept": "application/json, text/javascript, */*; q=0.01",
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
},
})
.then(response => response.json())
.then(result => {
const text = `<div>Найдено ${result.count} элементов(та)</div><button class="btn filter__btn">Применить</button>`;
this.popup = this.createElt(obj, 'div', 'filter__popup', text);
obj.closest('label').appendChild(this.popup);
setTimeout(() => this.destroy(this.popup), 3500);
});
}
// собираем результаты формы в Json
_serializeObject() {
const formData = new FormData(this.form);
let filter = 'fast_filter=1&';
formData.forEach((value, key) => {
filter += `${key}=${value}&`;
});
return encodeURI(filter.slice(0, -1));
}
deleteFilter(evt) {
if (!evt.target.classList.contains('filters-chosen__item')) return;
this.filterChosen = evt.target;
[...this.inputs].find(item => item.dataset.value === this.filterChosen.dataset.id).checked = false;
this.filterApply();
}
createElt(elt, eltType, className, text) {
const newElt = document.createElement(eltType);
newElt.classList.add(className);
newElt.tabindex = 0;
newElt.dataset.id = elt.dataset.value;
newElt.innerHTML = text;
return newElt;
}
openHandler() {
this.changeText();
this.targetElement = this.toggler.parentNode;
this.targetElement.classList.toggle('closed');
}
changeText() {
if (!this.toggler.dataset.text) return;
this.toggler.dataset.text === 'more' ? this.text = 'Отметить все' : this.text = 'снфть все отметки';
this.toggler.innerHTML = '';
this.toggler.innerHTML = this.text;
this.toggler.dataset.text === 'more' ? this.toggler.dataset.text = 'less' : this.toggler.dataset.text = 'more';
}
destroy(elt) {
elt.parentNode.removeChild(elt);
elt = null;
}
}
export class Filter {
constructor(container) {
this.container = container;
this.togglerClass ='js-toggler';
this.form = this.container.querySelector('form');
// переменные для построения пути фильтра
this.path = this.form.action;
this.priceFrom = null;
this.priceTo = null;
this.priceFromOriginal = null;
this.priceToOriginal = null;
this.producerField = this.form.querySelector('select[name = producer_id]');
this.priceFromBox = this.form.querySelector('input[name = price_from]');
this.sortingSelect = this.form.sorting;
this.inputs = null;
this.producerOption = null;
this.sortingOption = null;
// переменные для открытия и закрытия, а также отметки подчиненных/управляющих инбоксов
this.toggler = null;
this.targetElement = null;
this.checkbox = null;
this.parentLabel = null;
this.childLabels = null;
this.parentInbox = null;
this.popup = null;
this.timerId = null; // таймер для быстрого фильтра
this.filterChosenBlock = document.querySelector('.js-filter-chosen');
this.filterChosen = null; // выбранный фильтр для удаления
this.text = 'Больше фильтров';
this.closeClass = 'js-close';
this.deleteFilter = this.deleteFilter.bind(this);
this.destroy = this.destroy.bind(this);
this.init();
this.setListeners();
}
init() {
this.inputs = this.form.querySelectorAll('[data-property]:not(div)');
// проверяем все инпуты и выставляем родительскому лэйблу состояния отмечен, неотмечен, неопределен
this.inputs
.forEach(input => {
if (input.classList.contains('check')) {
if (input.checked) {
const newElt = this.createElt(input, 'button', 'filters-chosen__item', input.nextElementSibling.innerHTML);
this.filterChosenBlock.prepend(newElt);
}
}
});
}
filterApply() {
if (this.producerField) {
this.producerOption = this.producerField.option.selected;
if (parseInt(this.producerOption.value)) this.path += `${this.producerOption.dataset.producer.toLowerCase()}/`;
}
let tag_name = null;
if (this.inputs) {
this.inputs.forEach(input => {
let type = input.type;
let value = null;
let setValue = false;
if (input.name !== void 0 && input.name.indexOf('_to') !== -1) return;
switch(type) {
case 'checkbox':
case'radio':
if (input.checked) {
value += input.value;
setValue = type != 'checkbox' ? true : input.name.indexOf('[]') !== -1;
}
break;
case 'option':
if (input.selected) {
value = input.value;
setValue = true;
}
break;
case 'text':
case 'hidden':
value = input.value;
setValue = true;
break;
}
if (value && input.dataset.property !== tag_name) {
tag_name = input.dataset.property.toLowerCase();
if (input.name !== void 0 && input.name.indexOf('_from') !== -1) {
this.path +='';
} else {
this.path += `${tag_name}/`;
}
}
if (setValue && value) {
if (input.name !== void 0 && input.name.indexOf('_from') !== -1) {
const value2 = input.closest('.filter__fieldset-inside').querySelector('input[name $= "_to"]').value;
this.path += `${tag_name}-${value}-${value2}/`;
} else {
this.path += input.dataset.value !== ''
? `${input.dataset.value.toString().toLowerCase()}/`
: `${value.toString().toLowerCase()}/`
}
}
});
}
if (this.priceFromBox) {
this.priceFrom = this.priceFromBox.value;
this.priceTo = this.form.querySelector('input[name = price_to]').value;
this.priceFromOriginal = this.form.querySelector('input[name = price_from_original]').value
this.priceToOriginal = this.form.querySelector('input[name = price_to_original]').value
if (this.priceFrom && this.priceTo
&& (this.priceFroOriginalm !== this.priceFrom || this.priceToOriginal !== this.pticeTo)) {
this.path += `price-${priceFrom}-${priceTo}/`;
}
}
if (this.sortingSelect) {
this.sortingOption = this.sortingSelect.options.selectedIndex;
if (this.sortingOption !== 0) {
const sortingOptionValue = this.sortingSelect.options[this.sortingOption].value;
this.path += `?sorting=${sortingOptionValue.toLowerCase()}`;
}
}
window.location.href = this.path;
}
setListeners() {
this.container.addEventListener('click', (evt) => {
this.toggler = evt.target.closest(`.${this.togglerClass}`);
if (this.toggler) {
evt.preventDefault();
evt.stopImmediatePropagation();
this.openHandler();
return;
}
this.checkbox = evt.target.closest(`.check`);
if (this.checkbox) {
this.__fastFilter(this.checkbox);
this.changeState(this.checkbox);
}
if (evt.target.classList.contains('filter__btn')) {
evt.preventDefault();
this.filterApply();
}
});
if (this.filterChosenBlock) {
this.filterChosenBlock.addEventListener('click', this.deleteFilter);
}
this.form.addEventListener('submit',(evt) => {
evt.preventDefault();
this.filterApply();
})
}
// определяем состояние родительского эл-та
determineState(elt) {
this.parentContainer = elt.closest('.filter__container--parent');
if (!this.parentContainer) return; // если нет родительского - возвращаемся
this.parentLabel = this.parentContainer.querySelector('.filter__label--parent'); // определяем управляющий лэйбел
this.parentInbox = this.parentLabel.querySelector('input'); // определяем управляющий инпут
this.getChildren(this.parentContainer); //получаем подчиненные инпуты
this.checkParentInput(); // отправляем на проверку для установления состояния
}
// получаем подсиненные инпуты
getChildren(elt) {
this.childLabels = elt.querySelector('div').querySelectorAll('.check');
}
// изменение состояния управляющего инпута при клике на дочерний или дочернего при щелчке на подчиненный
changeState(elt) {
this.parentContainer = elt.closest('.filter__container--parent'); // получаем ближайший управляющий контейнер
if (!this.parentContainer) return; //если его нет, завершаем цикл
this.parentLabel = this.parentContainer.querySelector('.filter__label'); // получаем управляющий лэйбел
this.parentInbox = this.parentLabel.querySelector('.check'); // получаем управляющий инпут
this.getChildren(this.parentContainer); // собираем дочерние инпуты
// если целевой элт является уравляющим, проставляем всем подчиненным ставим им то же состояние, что и родительским
if (this.parentInbox === elt) {
const labelState = this.parentInbox.checked;
[...this.childLabels].forEach(label => {
label.indeterminate = 0;
label.checked = labelState;
});
return;
}
// если это один из подчиненных эл-тов, перепроверяем состояние управляющий эл-т
this.determineState(elt);
}
// проверка управляющего инпута
checkParentInput() {
// считаем все отмеченные подчиненные инпуты
const checkedChildren = this.parentContainer.querySelector('div').querySelectorAll('.check:checked');
const checkedChildrenCount = checkedChildren.length;
// если все отмечены, отмечаем родительский
if (checkedChildrenCount === this.childLabels.length) {
this.parentInbox.indeterminate = 0;
this.parentInbox.checked = 'checked';
if (this.parentContainer.classList.contains('closed')) {
this.parentContainer.classList.toggle('closed');
}
return;
}
// если со всех снята отметка, снимаем отметку с родительского
if (checkedChildrenCount === 0) {
this.parentInbox.indeterminate = 0;
this.parentInbox.checked = 0;
return;
}
// если отмечены не все, ставим indeterminate
this.parentInbox.indeterminate = true;
if (this.parentContainer.classList.contains('closed')) {
this.parentContainer.classList.toggle('closed');
}
}
__fastFilter(obj) {
if (this.timerId) {
clearTimeout(this.timerId);
}
this.timerId = setTimeout(() => {this._loadJson(obj)}, 500);
}
_loadJson(obj) {
const data = this._serializeObject();
return fetch('./', {
method:'POST',
body: data,
dataType: 'json',
headers: {
"Accept": "application/json, text/javascript, */*; q=0.01",
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
},
})
.then(response => response.json())
.then(result => {
const text = `<div>Найдено ${result.count} элементов(та)</div><button class="btn filter__btn">Применить</button>`;
this.popup = this.createElt(obj, 'div', 'filter__popup', text);
obj.closest('label').appendChild(this.popup);
setTimeout(() => this.destroy(this.popup), 3500);
});
}
// собираем результаты формы в Json
_serializeObject() {
const formData = new FormData(this.form);
let filter = 'fast_filter=1&';
formData.forEach((value, key) => {
filter += `${key}=${value}&`;
});
return encodeURI(filter.slice(0, -1));
}
deleteFilter(evt) {
if (!evt.target.classList.contains('filters-chosen__item')) return;
this.filterChosen = evt.target;
[...this.inputs].find(item => item.dataset.value === this.filterChosen.dataset.id).checked = false;
this.filterApply();
}
createElt(elt, eltType, className, text) {
const newElt = document.createElement(eltType);
newElt.classList.add(className);
newElt.tabindex = 0;
newElt.dataset.id = elt.dataset.value;
newElt.innerHTML = text;
return newElt;
}
openHandler() {
this.changeText();
this.targetElement = this.toggler.parentNode;
this.targetElement.classList.toggle('closed');
}
changeText() {
if (!this.toggler.dataset.text) return;
this.toggler.dataset.text === 'more' ? this.text = 'Отметить все' : this.text = 'снфть все отметки';
this.toggler.innerHTML = '';
this.toggler.innerHTML = this.text;
this.toggler.dataset.text === 'more' ? this.toggler.dataset.text = 'less' : this.toggler.dataset.text = 'more';
}
destroy(elt) {
elt.parentNode.removeChild(elt);
elt = null;
}
}
hostcms
Модератор
24 февраля 2021 г.
maria.ats,
верно ли, что вопрос решен?
верно ли, что вопрос решен?
Авторизация