Занимаюсь углубленным изучением JS. Написал простенький класс для автоматического позиционирования absolute
элементов относительно любого блока при клике.
Вопросов в принципе 3:
Использование
let posConfig = {
admin: {
btn: ".adminBtn",
btnPos: "bottom-right",
dropdown: ".adminDrop",
dropdownPos: "top-right",
animate: "fadeInDrop"
},
notification: {
btn: ".notificationBtn",
btnPos: "bottom-right",
dropdown: ".notificationWrapper",
dropdownPos: "top-right",
animate: "fadeInDrop"
},
message: {
btn: ".messageBtn",
btnPos: "bottom-right",
dropdown: ".messageWrapper",
dropdownPos: "top-right",
animate: "fadeInDrop"
}
};
new Positioner(posConfig);
Сам класс
export default class Positioner {
constructor(config){
this.elements = {};
Object.keys(config).map((k) => {
let obj = config[k];
let btn = document.querySelector(obj.btn);
let dropdown = document.querySelector(obj.dropdown);
this.elements[k] = {
btn: {
get: btn,
coordinates: obj.btnPos
},
dropdown: {
get: dropdown,
coordinates: obj.dropdownPos
},
animate: obj.animate,
show: false
};
this.elements[k].setPosition = () => this.setPosition(this.elements[k]);
this.addEvents(this.elements[k]);
});
return this.elements;
}
addEvents(obj){
window.addEventListener("click", () => this.setDefault(obj));
obj.btn.get.addEventListener("click", e => {
let currentObjectShow = obj.show;
Object.values(this.elements).filter(v => v.show).map(v => this.setDefault(v));
if(!currentObjectShow){
obj.show = true;
obj.setPosition();
e.stopPropagation();
}
}, false);
obj.dropdown.get.addEventListener("click", e => e.stopPropagation(), false);
}
setDefault(object){
Object.assign(object.dropdown.get.style,{top:"-100%",left:"-100%"});
object.dropdown.get.classList.remove(object.animate);
object.show = false;
}
getRect(element){
let rect = element.getBoundingClientRect();
return {
toTop: rect.top,
toBottom: rect.bottom,
toLeft: rect.left,
toRight: rect.right,
width: rect.width,
height: rect.height,
centerY: rect.top + rect.height / 2,
centerX: rect.left + rect.width / 2
}
}
setPosition(object){
let btn = object.btn;
let dropdown = object.dropdown;
let btnRect = this.getRect(btn.get);
let dropdownRect = this.getRect(dropdown.get);
let coordinateChild = this.calcPosition(0, 0, dropdownRect, dropdown.coordinates);
let coordinateParent = this.calcPosition(btnRect.toTop, btnRect.toLeft, btnRect, btn.coordinates);
let left = coordinateParent.x - coordinateChild.x;
let top = coordinateParent.y - coordinateChild.y;
this.fixPosition(object, top, left);
this.addAnimateClass(object);
}
calcPosition(modifyTop, modifyLeft, element, position){
let coordinate = {};
switch(position){
case 'top-left':
coordinate.x = modifyLeft;
coordinate.y = modifyTop;
break;
case 'top-center':
coordinate.x = modifyLeft + element.width / 2;
coordinate.y = modifyTop;
break;
case 'top-right':
coordinate.x = modifyLeft + element.width;
coordinate.y = modifyTop;
break;
case 'right-center':
coordinate.x = modifyLeft + element.width;
coordinate.y = modifyTop + element.height / 2;
break;
case 'bottom-right':
coordinate.x = modifyLeft + element.width;
coordinate.y = modifyTop + element.height;
break;
case 'bottom-center':
coordinate.x = modifyLeft + element.width / 2;
coordinate.y = modifyTop + element.height;
break;
case 'bottom-left':
coordinate.x = modifyLeft;
coordinate.y = modifyTop + element.height;
break;
case 'left-center':
coordinate.x = modifyLeft;
coordinate.y = modifyTop + element.height / 2;
break;
default:
coordinate.x = modifyLeft + element.width / 2;
coordinate.y = modifyTop + element.height / 2;
break;
}
return coordinate;
}
fixPosition(object, top, left){
let dropdownRect = this.getRect(object.dropdown.get);
let windowHeight = document.documentElement.clientHeight;
let windowWidth = document.documentElement.clientWidth;
let toBottom = top + dropdownRect.height;
let toRight = left + dropdownRect.width;
let newChildTop = windowHeight - dropdownRect.height;
let newChildLeft = windowWidth - dropdownRect.width;
object.dropdown.get.style.top = toBottom > windowHeight ? newChildTop+'px' : top;
object.dropdown.get.style.left = toRight > windowWidth ? newChildLeft+'px' : left;
}
addAnimateClass(object){
let dropdown = object.dropdown.get;
if(dropdown.classList.contains(object.animate)){
this.setDefault(object);
}
else{
dropdown.classList.add(object.animate);
}
}
}
Я бы переписал это как сервис.
Конструктор класса инициализирует свои переменные и навешивает глобальный эвент(чтобы не плодить их для каждого элемента).
this.elements = {};
document.addEventListener('click', () => {
Object.keys(this.elements).forEach(key => this.reset(key));
});
Метод добавления элемента
add(key, props) {}
Все методы, работающие с конкретными элементами на данный момент статические, не используют состояние класса(смысл всего класса при этом становится непонятным).
Это можно исправить, передавая в эти методы не сами элементы, а ключи.
Это избавит клиентский код от необходимости хранить у себя все элементы, достаточно будет хранить ключи.
fixPosition(key, top, left) {
const obj = this.elements[key];
//...
}
В итоге, как это будет выглядеть:
const positioner = new Positioner();
// Object.keys(posConfig).forEach(key => positioner.add(key, posConfig[key]));
positioner.add('admin', {
btn: {selector: '.btn', pos: {x: 'center', y: 'top'}}, ...
});
positioner.moveX('admin', 'btn', 'center');
Обратите внимание, что я разделил позицию элементов на вертикальную и горизонтальную. В текущей реализации у вас в switch 3*3=9 case, но в разных кейсах по частям происходит одно и то же. Это лучше в раздельных конструкциях по 3 варианта.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Всю голову сломал, делаю следующее: копирую текстовый контент в модальное окно-форму в textareaНеобходимо авто-изменение высоты textarea без события...
Я попробовал несколько библиотек, но ни одна не дала результатов, пробую вот это:
Проблема заключается в том, что как только я вкладываю картинку в папку JS ее не видитПроще говоря так работает:
хочу отренедрить разметку, после получения данных из fetch, при попытке отправить разметку, отправляется [object Promise]