Начальная вкладка из URL, удалённый HTML, пустой ответ, ошибка и JSON.
Каждый пример независим. Кнопка «Показать код» раскрывает HTML, JavaScript и CSS,
которые используются в самом примере. Стили оболочки и кнопок предоставляет OKAUX 1.0.2.
data-hash читает hash при создании экземпляра; это не двусторонний роутер и не обработчик hashchange.
Показать код
<p><a href="?open=hash#tabs-hash-1">Открыть страницу сразу на вкладке «Документы»</a>. Ссылка перезагружает страницу; обычное переключение вкладок URL не меняет.</p>
<div class="tabs-demo" id="tabs-hash-demo">
<div class="vg-tabs" data-hash="true">
<div class="vg-tabs-panel" role="tablist" aria-label="Разделы примера">
<button type="button" class="vg-tabs-link active" id="tabs-hash-0-tab" data-vg-toggle="tab" data-vg-target="#tabs-hash-0" aria-controls="tabs-hash-0">Сводка</button>
<button type="button" class="vg-tabs-link" id="tabs-hash-1-tab" data-vg-toggle="tab" data-vg-target="#tabs-hash-1" aria-controls="tabs-hash-1">Документы</button>
</div>
</div>
<div class="vg-tabs-content tabs-demo-content">
<div class="vg-tabs-pane show active" id="tabs-hash-0" tabindex="0">Сводка: содержимое самостоятельной панели.</div>
<div class="vg-tabs-pane" id="tabs-hash-1" tabindex="0">Документы: содержимое самостоятельной панели.</div>
</div>
</div>
/**
* Описание: декларативный пример VGTabs.
* Возможности: автоматическая обработка вкладок через Data API.
*/
// Дополнительная инициализация не требуется: используется Data API.
/* Токены OKAUX в демо настроены с префиксом vg; состояние темы общее. */
.tabs-demo { min-width: 0; }
.tabs-demo-content {
margin-top: 1rem;
padding: 1rem;
border: 1px solid var(--vg-border-default);
border-radius: var(--vg-radius-md);
background: var(--vg-bg-secondary);
overflow-wrap: anywhere;
}
.tabs-demo-custom {
--vg-tabs-panel-gap: 0.35rem;
--vg-tabs-link-padding-x: 0.75rem;
--vg-tabs-slider-border-radius: 999px;
--vg-tabs-track-slider-border-radius: 999px;
}
.tabs-demo-vertical {
display: grid;
grid-template-columns: 150px minmax(0, 1fr);
align-items: start;
gap: 1rem;
}
.tabs-demo-vertical > .tabs-demo-content { margin-top: 0; }
@media (max-width: 575.98px) {
.tabs-demo-vertical { grid-template-columns: minmax(0, 1fr); }
}
.tabs-demo-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-bottom: 1rem; }
.tabs-demo-status { margin-top: 1rem; }
.tabs-demo-log { max-width: 100%; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.85rem; }
Удалённый HTML и состояния
Реальный GET /api/tabs/demo. Задержка 400 мс задана через ajax.timeout для проверки загрузчика; ошибка намеренно возвращает HTTP 503.
Локально
Один раз
Каждый раз
Пусто
Ошибка
Выберите удалённую вкладку. Для повторного открытия сначала перейдите в другую.
Контент ещё не загружен.
Контент ещё не загружен.
Ожидание ответа.
Ожидание ответа.
Запросов завершено: 0
Показать код
<div class="tabs-demo" id="tabs-ajax-demo">
<div class="vg-tabs">
<div class="vg-tabs-panel" role="tablist" aria-label="Разделы примера">
<button type="button" class="vg-tabs-link active" id="tabs-ajax-0-tab" data-vg-toggle="tab" data-vg-target="#tabs-ajax-0" aria-controls="tabs-ajax-0">Локально</button>
<button type="button" class="vg-tabs-link" id="tabs-ajax-1-tab" data-vg-toggle="tab" data-vg-target="#tabs-ajax-1" aria-controls="tabs-ajax-1" data-ajax-route="/api/tabs/demo?mode=html" data-ajax-target="#tabs-ajax-1" data-ajax-loader="true" data-ajax-timeout="400">Один раз</button>
<button type="button" class="vg-tabs-link" id="tabs-ajax-2-tab" data-vg-toggle="tab" data-vg-target="#tabs-ajax-2" aria-controls="tabs-ajax-2" data-ajax-route="/api/tabs/demo?mode=html" data-ajax-target="#tabs-ajax-2" data-ajax-once="false" data-ajax-loader="true" data-ajax-timeout="400">Каждый раз</button>
<button type="button" class="vg-tabs-link" id="tabs-ajax-3-tab" data-vg-toggle="tab" data-vg-target="#tabs-ajax-3" aria-controls="tabs-ajax-3" data-ajax-route="/api/tabs/demo?mode=empty" data-ajax-target="#tabs-ajax-3" data-ajax-loader="true">Пусто</button>
<button type="button" class="vg-tabs-link" id="tabs-ajax-4-tab" data-vg-toggle="tab" data-vg-target="#tabs-ajax-4" aria-controls="tabs-ajax-4" data-ajax-route="/api/tabs/demo?mode=error" data-ajax-target="#tabs-ajax-4" data-ajax-loader="true">Ошибка</button>
</div>
</div>
<div class="vg-tabs-content tabs-demo-content">
<div class="vg-tabs-pane show active" id="tabs-ajax-0" tabindex="0">Выберите удалённую вкладку. Для повторного открытия сначала перейдите в другую.</div>
<div class="vg-tabs-pane" id="tabs-ajax-1" tabindex="0">Контент ещё не загружен.</div>
<div class="vg-tabs-pane" id="tabs-ajax-2" tabindex="0">Контент ещё не загружен.</div>
<div class="vg-tabs-pane" id="tabs-ajax-3" tabindex="0">Ожидание ответа.</div>
<div class="vg-tabs-pane" id="tabs-ajax-4" tabindex="0">Ожидание ответа.</div>
</div>
</div>
<p class="tabs-demo-status" id="tabs-ajax-status" role="status">Запросов завершено: 0</p>
/**
* Описание: AJAX-состояния VGTabs.
* Возможности: счётчик ответов, пустое состояние и повтор после ошибки.
*/
function initTabsDemo() {
const root = document.querySelector('#tabs-ajax-demo');
if (!root || root.dataset.ready) return;
root.dataset.ready = 'true';
let completed = 0;
root.addEventListener('vg.tabs.loaded', event => {
const panel = document.querySelector(event.target.getAttribute('data-vg-target'));
const response = event.data?.response;
if (event.stats === 'error') {
panel.textContent = 'Тестовая ошибка HTTP 503. Переключитесь в другую вкладку и вернитесь, чтобы повторить запрос.';
} else if (response === '') {
panel.textContent = 'Записей пока нет. Сервер вернул пустую строку.';
}
document.querySelector('#tabs-ajax-status').textContent =
'Запросов завершено: ' + (++completed) + '. Последний результат: ' + event.stats;
});
}
initTabsDemo();
/* Токены OKAUX в демо настроены с префиксом vg; состояние темы общее. */
.tabs-demo { min-width: 0; }
.tabs-demo-content {
margin-top: 1rem;
padding: 1rem;
border: 1px solid var(--vg-border-default);
border-radius: var(--vg-radius-md);
background: var(--vg-bg-secondary);
overflow-wrap: anywhere;
}
.tabs-demo-custom {
--vg-tabs-panel-gap: 0.35rem;
--vg-tabs-link-padding-x: 0.75rem;
--vg-tabs-slider-border-radius: 999px;
--vg-tabs-track-slider-border-radius: 999px;
}
.tabs-demo-vertical {
display: grid;
grid-template-columns: 150px minmax(0, 1fr);
align-items: start;
gap: 1rem;
}
.tabs-demo-vertical > .tabs-demo-content { margin-top: 0; }
@media (max-width: 575.98px) {
.tabs-demo-vertical { grid-template-columns: minmax(0, 1fr); }
}
.tabs-demo-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-bottom: 1rem; }
.tabs-demo-status { margin-top: 1rem; }
.tabs-demo-log { max-width: 100%; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.85rem; }
JSON без автоматического вывода
ajax.output=false оставляет рендер приложению. Поле event.stats содержит success или error, event.data.response — тело ответа.
Ответ JSON обрабатывается приложением без автоматической вставки HTML.
Данные ещё не загружены.
Показать код
<div class="tabs-demo" id="tabs-output-demo">
<div class="vg-tabs">
<div class="vg-tabs-panel" role="tablist" aria-label="Разделы примера">
<button type="button" class="vg-tabs-link active" id="tabs-output-0-tab" data-vg-toggle="tab" data-vg-target="#tabs-output-0" aria-controls="tabs-output-0">Описание</button>
<button type="button" class="vg-tabs-link" id="tabs-output-1-tab" data-vg-toggle="tab" data-vg-target="#tabs-output-1" aria-controls="tabs-output-1" data-ajax-route="/api/tabs/demo?mode=json" data-ajax-output="false">Данные</button>
</div>
</div>
<div class="vg-tabs-content tabs-demo-content">
<div class="vg-tabs-pane show active" id="tabs-output-0" tabindex="0">Ответ JSON обрабатывается приложением без автоматической вставки HTML.</div>
<div class="vg-tabs-pane" id="tabs-output-1" tabindex="0">Данные ещё не загружены.</div>
</div>
</div>
/**
* Описание: ручной вывод ответа VGTabs.
* Возможности: обработка JSON при ajax.output=false.
*/
function initTabsDemo() {
const root = document.querySelector('#tabs-output-demo');
if (!root || root.dataset.ready) return;
root.dataset.ready = 'true';
root.addEventListener('vg.tabs.loaded', event => {
document.querySelector('#tabs-output-1').textContent = event.stats === 'success'
? 'Открытых задач: ' + event.data.response.tasks + '. Источник: ' + event.data.response.source
: 'Не удалось загрузить данные.';
});
}
initTabsDemo();
/* Токены OKAUX в демо настроены с префиксом vg; состояние темы общее. */
.tabs-demo { min-width: 0; }
.tabs-demo-content {
margin-top: 1rem;
padding: 1rem;
border: 1px solid var(--vg-border-default);
border-radius: var(--vg-radius-md);
background: var(--vg-bg-secondary);
overflow-wrap: anywhere;
}
.tabs-demo-custom {
--vg-tabs-panel-gap: 0.35rem;
--vg-tabs-link-padding-x: 0.75rem;
--vg-tabs-slider-border-radius: 999px;
--vg-tabs-track-slider-border-radius: 999px;
}
.tabs-demo-vertical {
display: grid;
grid-template-columns: 150px minmax(0, 1fr);
align-items: start;
gap: 1rem;
}
.tabs-demo-vertical > .tabs-demo-content { margin-top: 0; }
@media (max-width: 575.98px) {
.tabs-demo-vertical { grid-template-columns: minmax(0, 1fr); }
}
.tabs-demo-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-bottom: 1rem; }
.tabs-demo-status { margin-top: 1rem; }
.tabs-demo-log { max-width: 100%; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.85rem; }