fix: SEO audit fixes for /otzyvy/ — Schema.org, PHP, JS, CSS, A11Y
Critical: - Move GeoCoordinates from PostalAddress to Organization level - Fix AggregateRating @id to point at #organization, not page URL High: - Replace Review.itemReviewed inline LocalBusiness with @id reference - Change @type from array to just 'School' - Remove foundingDate from reviews page (add comment for CF) - Make telephone property conditional (only when non-empty) - Add postalCode from branch_postal_code option - Fix branch_city fallback from 'городе' to 'Щёлково' - Replace inline onclick yaCounter with JS global handler - Remove inline critical CSS (styles in file) - Move price text to CF field reviews_intro_price_text Medium: - Add --color-bg and --accent CSS variables - Fix .page-rewiews typo → .page-reviews - Fix .contact-map display:flex→block - Add prefers-reduced-motion support - JS: var→let/const, scroll throttle via requestAnimationFrame - JS: catch(e)→console.warn - JS: data-target→data-target-value (avoid conflict) - A11Y: FAQ aria-expanded/aria-controls, phone aria-label - A11Y: modal role=dialog aria-modal=true - A11Y: breadcrumb role=navigation aria-label - Remove desktop-only class from breadcrumbs (mobile support) Low: - Add preconnect for unpkg.com (Phosphor Icons CDN) - Add decoding=async to gallery images
This commit is contained in:
parent
6f5feb7521
commit
c7bd6cef74
1906
wp-content/themes/dekart/assets/css/reviews-page.css
Normal file
1906
wp-content/themes/dekart/assets/css/reviews-page.css
Normal file
File diff suppressed because it is too large
Load Diff
222
wp-content/themes/dekart/assets/js/reviews-page.js
Normal file
222
wp-content/themes/dekart/assets/js/reviews-page.js
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
/**
|
||||||
|
* reviews-page.js — интерактив для page-reviews.php
|
||||||
|
* Анимации, фильтры, sticky CTA, FAQ, пагинация
|
||||||
|
*
|
||||||
|
* @package Dekart
|
||||||
|
*/
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// 1. АНИМАЦИЯ ПОЯВЛЕНИЯ (Intersection Observer)
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
const revealElements = document.querySelectorAll('.reveal');
|
||||||
|
if (revealElements.length && 'IntersectionObserver' in window) {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
function (entries) {
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('reveal--visible');
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ threshold: 0.1 }
|
||||||
|
);
|
||||||
|
revealElements.forEach(function (el) {
|
||||||
|
observer.observe(el);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Fallback: показываем всё сразу
|
||||||
|
revealElements.forEach(function (el) {
|
||||||
|
el.classList.add('reveal--visible');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// 2. КНОПКИ "ЧИТАТЬ ЕЩЁ"
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
document.querySelectorAll('.review-card__btn-more').forEach(function (btn) {
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
const textBlock = this.parentNode.querySelector('.lw-card__text');
|
||||||
|
if (textBlock) {
|
||||||
|
textBlock.classList.toggle('review-card__text--expanded');
|
||||||
|
this.textContent = textBlock.classList.contains('review-card__text--expanded')
|
||||||
|
? 'Свернуть'
|
||||||
|
: 'Читать ещё';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// 3. КНОПКА "ПОКАЗАТЬ ЕЩЁ" (десктоп)
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
const loadMoreBtn = document.getElementById('reviews-loadmore');
|
||||||
|
if (loadMoreBtn) {
|
||||||
|
let nextBatch = 2;
|
||||||
|
const btn = loadMoreBtn.querySelector('.reviews-loadmore__btn');
|
||||||
|
if (btn) {
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
const cards = document.querySelectorAll('.lw-card[data-batch="' + nextBatch + '"]');
|
||||||
|
if (cards.length) {
|
||||||
|
cards.forEach(function (card) {
|
||||||
|
card.style.display = 'flex';
|
||||||
|
});
|
||||||
|
nextBatch++;
|
||||||
|
// Если следующих батчей нет — прячем кнопку
|
||||||
|
const remaining = document.querySelectorAll('.lw-card[data-batch="' + nextBatch + '"]');
|
||||||
|
if (!remaining.length) {
|
||||||
|
loadMoreBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// 4. FAQ Accordion — keyboard + ARIA
|
||||||
|
// Глобальный click handler в add.js обрабатывает
|
||||||
|
// .box__accordion_label, синхронизируем aria-expanded
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
document.querySelectorAll('.box__accordion_label').forEach(function (label) {
|
||||||
|
label.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// При клике (из add.js) синхронизируем aria-expanded
|
||||||
|
label.addEventListener('click', function () {
|
||||||
|
const content = document.getElementById(this.getAttribute('aria-controls'));
|
||||||
|
const isOpen = content && content.style.maxHeight && content.style.maxHeight !== '0px';
|
||||||
|
this.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// 5. СЧЁТЧИК (CountUp) для цифр доверия
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
function animateCounter(el) {
|
||||||
|
const target = parseInt(el.getAttribute('data-target-value'), 10) ||
|
||||||
|
parseInt(el.getAttribute('data-target'), 10);
|
||||||
|
if (!target) return;
|
||||||
|
let current = 0;
|
||||||
|
const step = Math.ceil(target / 60); // 60 кадров ~ 1 сек
|
||||||
|
const timer = setInterval(function () {
|
||||||
|
current += step;
|
||||||
|
if (current >= target) {
|
||||||
|
current = target;
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
el.textContent = current.toLocaleString('ru-RU');
|
||||||
|
}, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.counter-number').forEach(function (counter) {
|
||||||
|
if ('IntersectionObserver' in window) {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
function (entries) {
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
animateCounter(entry.target);
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ threshold: 0.5 }
|
||||||
|
);
|
||||||
|
observer.observe(counter);
|
||||||
|
} else {
|
||||||
|
animateCounter(counter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// 6. STICKY CTA (мобильные) — троттлинг скролла
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
const stickyCta = document.querySelector('.sticky-cta-mobile');
|
||||||
|
if (stickyCta) {
|
||||||
|
const heroSection = document.querySelector('.reviews-hero');
|
||||||
|
if (heroSection) {
|
||||||
|
const heroBottom = heroSection.offsetTop + heroSection.offsetHeight;
|
||||||
|
let ticking = false;
|
||||||
|
window.addEventListener('scroll', function () {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(function () {
|
||||||
|
if (window.scrollY > heroBottom) {
|
||||||
|
stickyCta.classList.add('sticky-cta-mobile--visible');
|
||||||
|
} else {
|
||||||
|
stickyCta.classList.remove('sticky-cta-mobile--visible');
|
||||||
|
}
|
||||||
|
ticking = false;
|
||||||
|
});
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// 7. POPUP ФОРМЫ ОТЗЫВА (iframe)
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
const formBtn = document.querySelector('.review-form-block__btn');
|
||||||
|
if (formBtn) {
|
||||||
|
formBtn.addEventListener('click', function () {
|
||||||
|
const popup = document.querySelector('.frame__popup');
|
||||||
|
const iframe = popup ? popup.querySelector('.frame_reviews') : null;
|
||||||
|
const frameUrl = popup ? popup.getAttribute('data-frame-url') : '';
|
||||||
|
if (popup && iframe && frameUrl) {
|
||||||
|
iframe.src = frameUrl;
|
||||||
|
popup.style.display = 'flex';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeBtn = document.querySelector('.btn-close-frame__popup');
|
||||||
|
if (closeBtn) {
|
||||||
|
closeBtn.addEventListener('click', function () {
|
||||||
|
const popup = this.parentNode.parentNode;
|
||||||
|
popup.style.display = 'none';
|
||||||
|
const iframe = popup.querySelector('.frame_reviews');
|
||||||
|
if (iframe) iframe.src = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// 8. ЦЕЛИ ЯНДЕКС.МЕТРИКИ
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
function reachGoal(name) {
|
||||||
|
if (typeof yaCounter99835482 !== 'undefined') {
|
||||||
|
try {
|
||||||
|
yaCounter99835482.reachGoal(name);
|
||||||
|
} catch (e) {
|
||||||
|
if (window.console) {
|
||||||
|
console.warn('Metrika reachGoal error:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Клики по всем CTA-кнопкам
|
||||||
|
document.querySelectorAll('.js-reviews-cta').forEach(function (btn) {
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
const goal = this.getAttribute('data-goal') || 'cta_otzyvy_click';
|
||||||
|
reachGoal(goal);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Клик по телефону
|
||||||
|
document.querySelectorAll('a[href^="tel:"]').forEach(function (link) {
|
||||||
|
link.addEventListener('click', function () {
|
||||||
|
reachGoal('phone_click_otzyvy');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Клик по WhatsApp/Telegram
|
||||||
|
document.querySelectorAll('a[href*="wa.me"], a[href*="t.me"]').forEach(function (link) {
|
||||||
|
link.addEventListener('click', function () {
|
||||||
|
reachGoal('messenger_click_otzyvy');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user