Документация
Objs — это библиотека с открытым исходным кодом, которая может получать и изменять элементы DOM, управлять их состоянием и событиями, отправлять запросы AJAX, загружать и кэшировать сценарии, стили, изображения и функции тестирования (модульные и асинхронные тесты). Кроме того, это всего 10 КБ без каких-либо зависимостей.
Использование
Просто включите скрипт или импортируйте с помощью NPM и вызовите любую нужную вам функцию. Используйте левую или нижнюю навигацию (на мобильных устройствах), чтобы найти нужную тему или изучить примеры на главной странице.
Актуальная версия: v1.0 02/2023
Селектор / Получение элементов DOM
Используйте o('selector') для выбора таких элементов, как при querySelectorAll. Если вам нужен только первый элемент, используйте o.first(selector). Также он может получить элемент DOM или массив элементов o(elements) или объект self в состоянии. Функция возвращает объект с методами Objs и элементами DOM - подробнее ниже.
// получает все элементы и возвращает объект Objs
const objElements = o('.class');
// получает только первый элемент
const objElement = o.first('.class');
// получает новые элементы для работы
objElements.reset('.class2');
// запрос, чтобы найти всех дочерних элементов каждого элемента по селектору
objElement.find('.childClass');
// возвращает только первый дочерний элемент каждого элемента
objElement.first('.childClass');
// дать элемент или массив [] элементов, чтобы установить их для работы
const objElement = o(document.getElementById('name'));
Есть несколько способов получить элементы DOM из Objs, если вам нужно:
// возвращает все выбранные элементы DOM в виде массива [el0, ..]
o('.class').els;
// количество элементов
o('.class').length;
// первый DOM элемент
o('.class').el;
// последний DOM элемент
o('.class').last;
// Выбрать элементы из набора
// устанавливает элемент i DOM для операций (начинается с 0 индекса)
objElements.select(i).el;
// устанавливает последний элемент для работы
objElements.select().el;
// снова устанавливает управление всеми элементами DOM
objElements.all().els;
Чтобы удалить выбранные элементы из DOM или управлять списком объектов, используйте это:
// удаляет все элементы набора из DOM
objElements.remove();
// удаляет элемент i из DOM и возвращает Objs
objElements.remove(i);
// добавляет элемент в список операций и устанавливает атрибут oInit родителя
objElements.add(element);
// можно использовать селектор или массив элементов
objElements.add('.class2');
// удаляет элемент i из списка объектов для работы (не из DOM)
objElements.skip(i);
Если вы инициировали некоторые события с помощью .on(), вы должны инициировать их для добавленных элементов с помощью .onAll(). Если вам нужно отключить все события, например, когда отображается модальное окно, используйте .offAll(). Он просто отключает события, поэтому .onAll() снова активирует их.
objElements.add(element).onAll();
objElements.offAll();
Тесты
Прямое изменение DOM элементов
Есть несколько способов изменить все элементы или некоторые из них (используя .select(), .skip() и т.п.)
Изменение атрибутов
// любые атрибуты
// задание
o(selector).attr('attr', 'value');
// получение
o(selector).attr('attr');
// удаление
o(selector).attr('attr', '');
// получение всех атрибутов всех элементов как массива [{}, ...]
const attributes = o(selector).attrs();
// получение всех атрибутов выбранного элемента {}
const attributes = o(selector).select(3).attrs();
// dataset
// задание значений
o(selector).dataset({
action: "registration"
});
// получение как массива от всех элементов [{}, ...]
const datasets = o(selector).dataset();
// получение dataset только выбранного элемента {}
const dataset = o(selector).select(3).dataset();
// style атрибут
// задание как строки
o(selector).style('value');
// задание как массива свойств
o(selector).css({
height: "100px",
"max-width": "100px",// используй "" для свойств с -
});
// получение innerHTML всех элементов одной строкой
o(selector).innerHTML();
Есть специальные функции для управления атрибутом class.
// прямая установка значения атрибута
o(selector).setClass('class1 class2');
// добавление класса всем элементам
o(selector).addClass('class');
// удаление
o(selector).removeClass('class');
// переключение наличия и отсутствия "class"
o(selector).toggleClass('class');
// переключение с условием
o(selector).toggleClass('class', isActive);
// возвращает false, если какой-либо или выбранный элемент не имеет класса
const changed = o(selector).haveClass('class');
Чтобы добавить HTML или элементы в DOM, вы можете использовать эти функции. Функции добавления получают селектор или элемент DOM.
// задание inner HTML
o(selector).innerHTML('html');
o(selector).html('html');
// inner text
o(selector).innerText('text');
// inner text content
o(selector).textContent('text');
// создание элемента для примера ниже. Подробнее в разделе Управление состояниями
const objs = o.init(states).render();
// вывод как последнего(-их) дочерних элементов
// в первом найденом по селектору/указанном элементе
objs.appendInside('#root');
// вывод до и после
objs.appendBefore(element);
objs.appendAfter('#child');
Если вам нужно управлять элементами, используйте .forEach(function) - получает те же параметры, что и состояние: o, self, i-индекс элемента.
o(selector).forEach(({self, i}) => {
// self.els[i] - каждый управляемый элемент
});
Тесты
Events
Существуют синонимы для стандартных методов событий, но они могут получать несколько событий, разделенных ', ' (запятая и пробел). Проверьте event.target (например, его classList) в прослушивателе, чтобы проверить, запущено ли событие общего документа правильным элементом.
// синоним addEventListener с поддержкой нескольких событий
o(selector).on('event1, event2', listener, options);
// синоним(removeEventListener)
o(selector).off('event1, event2', listener, options);
Если вы используете .select() перед включением/выключением, это будет выполнено только для выбранного элемента.
Иногда необходимо отключить все события на некоторое время. Для этого используйте методы ниже.
// выключение click слушателей
o(selector).offAll('click');
// выключение всех событий
o(selector).offAll();
// включение
o(selector).onAll();
Все инициированные события сохраняются в специальном параметре .ie - он сохраняет каждый обработчик каждого события для их включения/выключения. Вы можете получить оттуда функции и инициализировать их на других элементах.
// первая (0) функция события клика в массиве
objs.ie.click[0]
// содержит массив:
// 0: function, 1: options or useCapture or undefined, 2: wantsUntrusted if was set or undefined
// копировать события кликов в другие элементы с параметрами
objs.ie.click.forEach(handler => {
o(selector).on('click', ...handler);
});
Тесты
Управление состояниями DOM элементов
Одна из особенностей Objs — состояния. Он получает специальный объект коллекции состояний и переключает элементы между ними. Он разделяет логику и представление с помощью образцов. Образцы крупнее компонентов и позволяют работать с модулями без микроразделения.
Объект общих состояний
Это объект с состояниями – значениями типа object / string / function.
Если элемент нужно создать - объект состояний должен содержать состояние render для создания. Установите tag с именем тега, иначе он будет установлен как 'div'.
// общая структура
const states = {
// зарезервированное название для создания элемента
render: {
tag: "div",// название тега
html: "string",
class: "string",
// можно ничего не возвращать
events: ({self}) => {
self.on('click', handler);
},
// другие атрибуты
},
// состояние для изменения элемента
stateName: {
attribute: "string",
// функция возвращает значение атрибута
class: ({disabled}) => {
return `link ${disabled ? 'hidden' : ''}`;
},
},
// состояния включения/выключения событий
startEventsName: ({self}) => {
self.onAll('click');
},
pauseEventsName: ({self}) => {
self.offAll('click');
}
}
Чтобы добавить потомков, используйте атрибут append в состоянии с передачей DOM элемента, Objs объекта или их массивом.
Используйте строку HTML от верстальщика вместо объекта в качестве состояния 'render', чтобы инициализировать элементы еще быстрее. Чтобы использовать динамический контент, установите состояние рендеринга как функцию, которая возвращает строку HTML. Если элементов несколько - они будут в контейнере div.
const states = {
render: (props) => {
return `
<h3>HTML ${props.title} здесь</h3>
<p>Оба тега будут в одном DIV и
он будет управляться, а не эти теги</p>
`;
},
shown: {
removeClass: 'hidden',
},
hidden: {
addClass: 'hidden',
}
}
Если вам нужна быстрая инициализация и рендеринг, используйте .initState(state, data) — он создает или изменяет элемент для состояния путем его немедленного рендеринга. Он равен .init(state).render(data)
// создает элемент
const link = o.initState({
tag: 'a',
href: '/',
innerText: 'Main page'
});
const activeState = {addClass: 'active'};
// быстрое изменение состояния
link.initState(activeState);
Создание элементов
В зависимости от вашей архитектуры можно использовать несколько типов значений состояний. Самый простой - это просто строка как состояние без имени состояния. Рекомендуется использовать объект состояний с состояниями, чтобы быть уверенным в инициализации элементов и возможностях управления, например. Метод .render() может создать элемент для каждых данных в массиве.
Чтобы создать элемент, используйте o.init(state).render(), а затем добавьте его как HTML или специальным методом. Без добавления элемент не появится в DOM.
// вставка как HTML
o('#root').innerHTML(o.init(states).render().html());
// вставка как элемента
o.init(states).render().appendInside('#root');
// вставка другими методами
objs.appendBefore(element);
objs.appendAfter('#child');
Состояние как строка
// HTML строка
o.init('<p>String</p>').render();
Состояние как объект
o.init({// состояние
tag: 'img',
src: ({src, utm}) => {
return src + '?utm=' + utm;
},
alt: ({alt}) => {return alt}
}).render(props);
Функция состояния возвращает строку и .render(data) содержит информацию для 2 элементов, чтобы создать 2 и добавить в DOM.
// функция возвращает HTML строку
o.init(({title, text, data, id}) => {
return `
<div id="article${id}" class="article">
<h2>${title}</h2>
<p>${text}<br>
<hr>
${data}</p>
</div>
`;
}).render([// автоматически создаются 2 элемента
{
id: 1,
title: 'Название 1',
text: 'Текст статьи',
data: '12/13/2022'
},
{
id: 2,
title: 'Название 2',
text: 'Текст статьи',
data: '12/13/2022'
},
]).appendInside('.o-articles');// вставит в первый ".o-articles" элемент
Рекомендуется использовать объект состояний для создания и управления элементами.
Каждая функция состояния и атрибута получает self, o, i в параметрах: для инициированного объекта (self), функцию o и индекс текущего элемента, например, для событий, чтобы контролировать специальный элемент, а не все из них.
const banner = o.init({// состояния
render: {
tag: 'img',
class: 'banner-img banner-hidden',
src: ({src, utm}) => {
return src + '?utm=' + utm;
},
alt: ({alt}) => {return alt}
},
shown: {
removeClass: 'banner-hidden'
},
events: ({self}) => {
document.addEventListener('scroll', () => {
self.shown();
})
}
}).render(props).events();
Переключение состояний
Чтобы переключить состояния существующего элемента, вы просто выбираете его с помощью o(), загружаете состояния с помощью .init() и используете методы, названные как состояния, чтобы легко переключать элементы между ними. После каждой операции он возвращает Objs со всеми методами.
Чтобы изменить определенный элемент, используйте .select() перед переключением состояния. Если вам нужно изменить дочерний элемент, используйте o(self).find(selector), чтобы создать отдельный объект и управлять другими элементами.
// пример
// создание и вывод элемента
const menuID = o.init(menuStates)
.render(menuData)
.appendInside('#root')
.initID;
// изменение состояни где-то в другом месте
o(menuID).stiky();
// или с использованием селектора и специального метода
o.take('.class').stiky();
Если состояния элемента были инициализированы, они кэшируются, и все состояния доступны с помощью o(query) или o(initID) без новой инициализации - пример выше.
Пример
Полный пример объекта реального состояния с состоянием рендеринга и некоторыми другими.
// пример объекта состояний
const states = {
// состояние создания элемента
render: {
tag: 'button',// по умолчанию 'div' (если не указано)
class: 'button',// используй addClass для добавления и removeClass для удаления
html: 'Click me',
onclick: 'clickFunction()',
disabled: true
},
// подключение событий
events: ({self, o}) => {
// self это объект со всеми состояниями
self.on('mouseenter', (e) => {
o(e.target).active();// элемент создан и имеет все состояния
});
self.on('mouseleave', (e) => {
o(e.target).disabled();
});
},
// другие состояни и атрибуты для изменения
special: {
// использование функции для создания любого динамического содержимого
html: (props) => {return props.name},
disabled: (props) => {return Boolean(props.disabled)}
},
error: {
// есть o(), self, i в props для управления
showError: (props) => {
// задание текста ошибки
props.el.closest('.error-text').innerText = props.errorText;
},
disabled: true
},
active: {
disabled: false,
clearError: (props) => {
props.el.closest('.error-text').innerText = '';
}
},
disabled: {
disabled: true,
}
};
Если state является функцией, он получает аргумент props с функцией o, self который содержит все функции и инициированные элементы и i индексом текущего обрабатываемого элемента.
Если attribute является функцией, она получает аргумент props (данные общие для состояний, если не массив) с теми же o, self, i.
Есть специальные атрибуты редактирования: addClass, removeClass, toggleClass. Style атрибут может быть объектом style: {} для более удобного задания.
// инициализирует существующие состояния элементов и запускает функцию для событий привязки
const btn = o('.button').init(states).events();
// примерение состояния "active" для всех кнопок
btn.active();
// переключение состояния с передачей текста
btn.special({name: 'Special state', disabled: false});
// создает элемент который можно вывести в DOM
const newBtn = o.init(states).render().el;
// создает элементы для каждого набора данных в массивы
const newBtns = o.init(states).render([prop0, prop1]).el;
// можно передавать одно состояние, оно инициируется как render()
const newSameBtn = o.init({
tag: 'button',
class: 'button',
html: 'Click me',
onclick: 'clickFunction()',
disabled: true
}).render().el;
// можно использовать HTML для рендеринга элементов
const btnState = (props) => {
return `<button class="button">${props.text}</button>`;
};
const anotherBtn = o.init(btnState).render({text: 'Button 1'});
// если параметры - массив, то элемент будет создан для каждого
anotherBtn.reset().init(btnState).render([{text: 'Button 1'}, {text: 'Button 2'}]);
Создание состояний и HTML из DOM
// возвращает HTML код всех элементов целиком в строке
const classHTML = o('.class').html();
// возвращает HTML код созданного элемента
const newBtnHTML = o.init(states).render().html();
// создает объект состояний с состоянием "render" из первого элемента
const newStates = o('.button').sample();
// создание состояний из второго элемента
const newStates = o('.button').select(1).sample();
// пример клонирования объекта из DOM для его создания
o.initState({
...newStates,
html: ({title}) => {return title},
onclick: ({key}) => {globalSelect(key)},
},
[
{title: 'Btn 1', key: 'one'},
{title: 'Btn 2', key: 'two'},
]);
Можно получить элементы из DOM используя o(initID) где initID это параметр objs.initID, который появляется после init().
Тесты
Подгрузка и кеширования скриптов, стилей
Существует функция o.inc() для импорта сценариев JS/стилей CSS/изображений для модулей и состояний HTML. Также он создает кеш в localStorage, отправляя запрос GET (по умолчанию включен).
// параметры и значения по умолчанию
// установите false, чтобы отключить
o.incCache = true;
// 24 часа для хранения кеша
o.incCacheExp = 1000 * 60 * 60 * 24;
// timeOut для загрузки всех функций. После - отменяет обратный вызов и запускает функцию ошибки
o.incTimeOut = 6000;
// дополнение для src в начале
o.incSource = '';
// предотвращает повторное включение с одним и тем же идентификатором (если он был включен ранее)
o.incForce = false;
// установите значение false, если вам нужен порядок загрузки скриптов
o.incAsync = true;
// служебная информация
// массив всех статусов «ID: 1/0» для прямой проверки
o.incFns = {};
// массив состояний подгружаемых наборов
o.incSet = [0, ...];
Вот основная функция. Она возвращает ID набора загрузки из o.incSet[], где 1 для успешной загрузки всех скриптов и 0 при неудаче. Если скрипты в процессе загрузки, там будет функция для вызова по окончании. Если sources при вызове отсутствует - возвращает id последней загрузки.
const incId = o.inc({// возвращает ID для проверки статуса
'name': 'src'// ID имени и src для JS, CSS или файла изображения
}, callback, callbad);
// простая версия с массивом ссылок
o.inc([
'modal.js',
'modal.css',
'modalBackground.jpg',
], (setId) => {
// функция успеха получает установленный идентификатор, например, проверять статусы
});
o.incSource = 'scripts/';
o.inc({
pow: 'pow.js'// имя для идентификации и src для загрузки скрипта
powStyle: 'pow.css'// стили для модуля
banner: 'pow.jpg'// изображение для предварительной загрузки
}, () => {// функция для запуска после загрузки
console.log('Successful using script with pow()');
}, () => {// функция для запуска в случае сбоя с тайм-аутом
console.log('Loading failed');
});
// проверяет последний загруженный набор функций
if (o.incCheck(o.inc())) { ... }
// проверяет специальный набор по ID
if (o.incCheck(incId)) { ... }
// проверить загружен ли текущий скрипт, 'pow' - имя скрипта
if (o.incFns['pow']) { ... }
// очищает весь кеш localStorage
// если "all" true - очищает все системные параметры
o.incCacheClear(all);
Если o.inc() получает массив, не будет никакого управления кешем или проверки на повторение. Также нет кеша для ссылок, начинающихся с «http».
Тесты
GET и POST запросы
Есть несколько базовых функций промисов для отправки запросов. Можно использовать только один параметр - url. Они возвращают Promise, поэтому вы можете использовать .then() или await.
// простой GET
o.get(url)
.then();
// GET с объектом данных и JSON ответом
o.get(url, {
data: {
param1: 'value1',
// значение объекта будет преобразовано в строку JSON
param2: {
...
}
}
})
.then(response => response.json())
.then((response) => {
...
});
Для POST вы можете использовать параметр body для специальных данных или параметр data для автоматического создания строки данных.
// POST с объектом data и JSON ответом
o.post(url, {
data: {
param1: 'value1',
// значение объекта будет преобразовано в строку JSON
param2: {
...
}
}
})
.then(response => response.json())
.then((response) => {
...
});
Чтобы унифицировать использование запроса .ajax() - это может быть запрос GET или POST.
o.ajax(url, {
method: 'post',// должно быть 'post' или 'get'
// остальные параметры
})
.then(response => response.json())
.then((response) => {
...
});
Используйте o.getParams(), чтобы получить массив GET-параметров страницы.
const params = o.getParams();
Тесты
Unit тесты и их параметры
Тестовая функция унифицирована и получает название теста и тесты, а последним параметром может быть функция завершения. Запускается после тестов или по тайм-ауту. Журнал результатов может быть двух видов: в стиле консоли (по умолчанию) и в формате HTML (o.tStyles = true) — оба вы найдете ниже в тестовом разделе.
Тестовое выражение или функция должны возвращать true для успешной проверки. Если это false или какая-то строка - она помечается как ошибка и строка отображается как текст ошибки.
Для тестирования асинхронных функций, например. запросы - используйте o.testUpdate() с параметрами в функции проверки.
// параметры и значения по умолчанию
// установите значение true, чтобы также увидеть результаты успешного теста
o.tShowOk = false;
// установите значение true, чтобы результаты были оформлены в стиле HTML
o.tStyled = false;
// тайм-аут асинхронных тестов
o.tTime = 2000;
// массив всех тестовых сессий
// получить сеанс, который вы хотите, и проверить результаты или .join(), чтобы увидеть их все
o.tLog = [];
// массив истинных/ложных результатов всех тестовых сессий
o.tRes = [];
// массив массивов с истинными/ложными результатами каждого теста в сессиях
o.tStatus = [][];
// использование
o.test("session / function title",// основное название этой тестовой сессии
// массив с заголовком проверки и выражения/функции на истинность
["test/check title", func() === val],
["test/check title", () => {
// вернуть true в случае успеха и false или текст ошибки в случае неудачи
return true;
}],
// получить информацию для асинхронных тестов
["test/check title", (info) => {
// использование o.testUpdate для обновления статуса теста
setTimeout(() => {
o.testUpdate(info, true, ' - some additional text');
}, 100);
}],
// функция после всех тестов (или таймаута), получает номер сессии
(n) => {
console.log('Result: \n' + o.tLog[n]);
}
);
Существует функция, которая будет вызываться при ошибках во время выполнения функций Objs. Установите o.onError в качестве функции и отображайте или регистрируйте ошибки по мере необходимости. По умолчанию ошибки не отображаются.
o.onError = (error) => {
console.log(error);
}
Тесты
Примеры каждого вида теста:
tStyles: false; (console вид, но \n заменены на теги br)tStyles: true; (HTML)
Настройки отладки
Сообщения об ошибках по умолчанию отключены, но их можно включить изменив o.showErrors на true. Или использовать o.logErrors() чтобы увидеть все скрытые сообщения в консоли.
Чтобы сделать свой обработчик ошибок – задайте o.onError нужную функцию. Чтобы увидеть скрытые ошибки – их можно взять из массива o.errors.
o.errors.forEach(o.onError)
Ограничения
Состояния НЕ должны совпадать с методами или параметрами Objs ниже
length
el
els
last
ie
initID
initedEvents
Данные передаваемые состояниям не должны содержать элементы по ключам i, o, self.
События должны быть добавлены параметром self в состояниях, чтобы быть контролируемыми.
Состояния можно перезаписывать — они могут быть перезаписаны или перезаписаны параметрами. Будь осторожен.