Интерактивность на веб-странице

ArbNet
На сайте с 27.10.2019
Offline
138
2910

Представьте, вы составили HTML разметку для страницы и подключили JS модуль интерактивности, который автоматически добавляет обработчики событий на элементы. Пользователь кликает на кнопку добавляются другие элементы, открываются диалоговые окна, удаляются элементы, меняется стилизация и прочее. Вам не надо писать никакого JS кода. Согласитесь удобно и быстро создавать таким образом страницы сайта, без знания программирования, только HTML разметка.

Теперь вопрос к разработчикам кто может так сделать. У меня два варианта:

1) Добавлять события для каждого элемента отдельно. При этом возникают трудности при рендеринге(добавления на страницу фрагмента разметки), так как нужно отдельно делать обработку и добавлять события для элементов этого фрагмента разметки.

2) Сделать глобальные обработчики, которые будут перехватывать события на любом элементе и потом уже в зависимости от элемента делать обработку события. Тут не надо отдельно заморачиваться добавлением событий для каждого элемента.

У меня сейчас сделано по первому варианту, но я склоняюсь ко второму, т.к. столкнулся с проблемой добавления событий при вставки фрагмента разметки на страницу, получается не очень хорошо. Вот и задумался, может лучше переделать на второй вариант. Но пока не ясно какие подводные камни меня ждут.

M3
На сайте с 09.02.2022
Offline
84
#1
это никому не нужная суета, тем более разработчикам)
вот создал рабочий инет магазин, за минуту, 20 секунд написание промпта + 40 сек на генерацию кода:
с модальными окнами, корзиной и прочими фишками











ArbNet
На сайте с 27.10.2019
Offline
138
#2
Думаю второй вариант не лучше, т.к. возникнут проблемы с разными типами событий к тому же наблюдение(observe) за элементами проблемно делать будет, хотя для стандартных событий вариант супер просто.
ArbNet
На сайте с 27.10.2019
Offline
138
#3
master32 #:
это никому не нужная суета, тем более разработчикам

Сайты могут быть разными, сервисы, админ панели и прочее, про магазины и прочие шаблонные скрипты тут нет речи. Хотя можно будет и магазин с нестандартными функциями сделать какие захочется, а вот ИИ вряд-ли с таким справится. Частично может что-то и выдаст, но потом кучу времени потратишь на то чтобы переделать и сделать как хочется.

master32 #:
вот создал рабочий инет магазин, за минуту

Можно на код взглянуть?

M3
На сайте с 09.02.2022
Offline
84
#4
ArbNet #:

Сайты могут быть разными, сервисы, админ панели и прочее, про магазины и прочие фаблонные скрипты тут нет речи. Хотя можно будет и магазин с нестандартными функциями сделать какие захочется, а вот ИИ вряд-ли с таким справится. Частично может что-то и выдаст, но потом кучу времени потратишь на то чтобы переделать и сделать как хочется.

Можно на код взглянуть?

первый код я не сохранил)
вот повторно немного другое, конечно, но с нуля, глядя в 2010 год, это прям супер результат в кубе (для меня)

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Магазин товаров</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        .product-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
        }
        .product-card {
            transition: all 0.3s ease;
        }
        .cart-item {
            animation: fadeIn 0.5s;
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        .badge {
            position: absolute;
            top: -8px;
            right: -8px;
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Навигация -->
    <nav class="bg-white shadow-md sticky top-0 z-50">
        <div class="container mx-auto px-4 py-3 flex justify-between items-center">
            <div class="flex items-center space-x-2">
                <i class="fas fa-store text-indigo-600 text-2xl"></i>
                <span class="text-xl font-bold text-gray-800">ТоварыРус</span>
            </div>
            <div class="flex items-center space-x-4">
                <div class="relative" id="cart-icon">
                    <i class="fas fa-shopping-cart text-gray-600 text-xl cursor-pointer hover:text-indigo-600 transition"></i>
                    <span id="cart-count" class="badge bg-indigo-600 text-white text-xs font-bold rounded-full h-5 w-5 flex items-center justify-center">0</span>
                </div>
                <button class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md transition">
                    <i class="fas fa-user mr-2"></i>Войти
                </button>
            </div>
        </div>
    </nav>

    <!-- Основной контент -->
    <main class="container mx-auto px-4 py-8">
        <!-- Заголовок и фильтры -->
        <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-8">
            <h1 class="text-2xl font-bold text-gray-800 mb-4 md:mb-0">Наши товары</h1>
            <div class="flex flex-wrap gap-2">
                <select class="bg-white border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
                    <option>Сортировать по:</option>
                    <option>Популярности</option>
                    <option>Цене: от низкой</option>
                    <option>Цене: от высокой</option>
                    <option>Новизне</option>
                </select>
                <select class="bg-white border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
                    <option>Категория:</option>
                    <option>Электроника</option>
                    <option>Одежда</option>
                    <option>Дом</option>
                    <option>Спорт</option>
                </select>
            </div>
        </div>

        <!-- Товары -->
        <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6" id="products-container">
            <!-- Товары будут добавлены здесь динамически -->
        </div>

        <!-- Корзина (мобильная версия) -->
        <div id="mobile-cart" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
            <div class="absolute bottom-0 left-0 right-0 bg-white rounded-t-xl p-4 max-h-[80vh] overflow-y-auto">
                <div class="flex justify-between items-center mb-4">
                    <h2 class="text-xl font-bold">Корзина</h2>
                    <button id="close-mobile-cart" class="text-gray-500 hover:text-gray-700">
                        <i class="fas fa-times text-xl"></i>
                    </button>
                </div>
                <div id="mobile-cart-items" class="space-y-3 mb-4">
                    <!-- Товары в корзине будут добавлены здесь -->
                </div>
                <div class="border-t pt-4">
                    <div class="flex justify-between font-bold text-lg mb-4">
                        <span>Итого:</span>
                        <span id="mobile-cart-total">0 ₽</span>
                    </div>
                    <button id="mobile-checkout-btn" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white py-3 rounded-md transition disabled:bg-gray-400 disabled:cursor-not-allowed" disabled>
                        Заказать
                    </button>
                </div>
            </div>
        </div>

        <!-- Корзина (десктопная версия) -->
        <div id="cart-panel" class="fixed right-0 top-0 h-full bg-white shadow-lg w-80 transform translate-x-full transition-transform duration-300 ease-in-out z-40">
            <div class="p-4 h-full flex flex-col">
                <div class="flex justify-between items-center mb-4">
                    <h2 class="text-xl font-bold">Корзина</h2>
                    <button id="close-cart" class="text-gray-500 hover:text-gray-700">
                        <i class="fas fa-times text-xl"></i>
                    </button>
                </div>
                <div id="cart-items" class="flex-grow overflow-y-auto space-y-3 mb-4">
                    <!-- Товары в корзине будут добавлены здесь -->
                </div>
                <div class="border-t pt-4">
                    <div class="flex justify-between font-bold text-lg mb-4">
                        <span>Итого:</span>
                        <span id="cart-total">0 ₽</span>
                    </div>
                    <button id="checkout-btn" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white py-3 rounded-md transition disabled:bg-gray-400 disabled:cursor-not-allowed" disabled>
                        Заказать
                    </button>
                </div>
            </div>
        </div>
    </main>

    <!-- Заказ успешно оформлен (модальное окно) -->
    <div id="order-success" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
        <div class="bg-white rounded-lg p-6 max-w-md w-full">
            <div class="text-center">
                <div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-green-100 text-green-500 mb-4">
                    <i class="fas fa-check text-2xl"></i>
                </div>
                <h3 class="text-xl font-bold mb-2">Заказ успешно оформлен!</h3>
                <p class="text-gray-600 mb-4">Ваш заказ в процессе обработки. Спасибо за покупку!</p>
                <button id="close-success" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md transition">
                    Продолжить покупки
                </button>
            </div>
        </div>
    </div>

    <script>
        // Данные товаров
        const products = [
            { id: 1, name: "Смартфон X Pro", price: 49990, image: "https://picsum.photos/seed/phone1/300/300.jpg", articul: "SP-12345", category: "Электроника" },
            { id: 2, name: "Беспроводные наушники", price: 8990, image: "https://picsum.photos/seed/headphones2/300/300.jpg", articul: "HN-67890", category: "Электроника" },
            { id: 3, name: "Чехол для телефона", price: 999, image: "https://picsum.photos/seed/case3/300/300.jpg", articul: "CC-24680", category: "Аксессуары" },
            { id: 4, name: "Фитнес-трекер", price: 3490, image: "https://picsum.photos/seed/tracker4/300/300.jpg", articul: "FT-13579", category: "Спорт" },
            { id: 5, name: "Кофемашина", price: 12990, image: "https://picsum.photos/seed/coffee5/300/300.jpg", articul: "CM-98765", category: "Дом" },
            { id: 6, name: "Стиральная машина", price: 24990, image: "https://picsum.photos/seed/washer6/300/300.jpg", articul: "WM-54321", category: "Дом" },
            { id: 7, name: "Мужская куртка", price: 7990, image: "https://picsum.photos/seed/jacket7/300/300.jpg", articul: "JK-86420", category: "Одежда" },
            { id: 8, name: "Беговая дорожка", price: 32990, image: "https://picsum.photos/seed/treadmill8/300/300.jpg", articul: "TR-75310", category: "Спорт" }
        ];

        // Корзина
        let cart = [];

        // Отображение товаров
        function renderProducts() {
            const productsContainer = document.getElementById('products-container');
            productsContainer.innerHTML = '';

            products.forEach(product => {
                const productCard = document.createElement('div');
                productCard.className = 'product-card bg-white rounded-lg overflow-hidden shadow-md';
                productCard.innerHTML = `
                    <img src="${product.image}" alt="${product.name}" class="w-full h-48 object-cover">
                    <div class="p-4">
                        <h3 class="text-lg font-semibold mb-1">${product.name}</h3>
                        <p class="text-gray-500 text-sm mb-2">${product.articul}</p>
                        <div class="flex justify-between items-center">
                            <span class="text-indigo-600 font-bold">${product.price.toLocaleString()} ₽</span>
                            <button class="add-to-cart bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-1 rounded-md text-sm transition"
                                    data-id="${product.id}">
                                <i class="fas fa-cart-plus mr-1"></i>В корзину
                            </button>
                        </div>
                    </div>
                `;
                productsContainer.appendChild(productCard);
            });

            // Добавление обработчиков для кнопок "В корзину"
            document.querySelectorAll('.add-to-cart').forEach(button => {
                button.addEventListener('click', function() {
                    const productId = parseInt(this.getAttribute('data-id'));
                    addToCart(productId);
                    
                    // Эффект добавления в корзину
                    this.innerHTML = '<i class="fas fa-check mr-1"></i>Добавлено';
                    this.classList.remove('bg-indigo-600', 'hover:bg-indigo-700');
                    this.classList.add('bg-green-600', 'hover:bg-green-700');
                    
                    setTimeout(() => {
                        this.innerHTML = '<i class="fas fa-cart-plus mr-1"></i>В корзину';
                        this.classList.remove('bg-green-600', 'hover:bg-green-700');
                        this.classList.add('bg-indigo-600', 'hover:bg-indigo-700');
                    }, 1000);
                });
            });
        }

        // Добавление товара в корзину
        function addToCart(productId) {
            const product = products.find(p => p.id === productId);
            if (!product) return;

            const existingItem = cart.find(item => item.id === productId);
            if (existingItem) {
                existingItem.quantity += 1;
            } else {
                cart.push({
                    id: product.id,
                    name: product.name,
                    price: product.price,
                    image: product.image,
                    articul: product.articul,
                    quantity: 1
                });
            }

            updateCart();
        }

        // Удаление товара из корзины
        function removeFromCart(productId) {
            cart = cart.filter(item => item.id !== productId);
            updateCart();
        }

        // Обновление количества товара в корзине
        function updateQuantity(productId, newQuantity) {
            const item = cart.find(item => item.id === productId);
            if (item) {
                if (newQuantity <= 0) {
                    removeFromCart(productId);
                } else {
                    item.quantity = newQuantity;
                    updateCart();
                }
            }
        }

        // Обновление отображения корзины
        function updateCart() {
            const cartItems = document.getElementById('cart-items');
            const mobileCartItems = document.getElementById('mobile-cart-items');
            const cartCount = document.getElementById('cart-count');
            const cartTotal = document.getElementById('cart-total');
            const mobileCartTotal = document.getElementById('mobile-cart-total');
            const checkoutBtn = document.getElementById('checkout-btn');
            const mobileCheckoutBtn = document.getElementById('mobile-checkout-btn');

            // Обновление количества goods в иконке корзины
            const totalItems = cart.reduce((total, item) => total + item.quantity, 0);
            cartCount.textContent = totalItems;

            // Очистка текущего содержимого корзины
            cartItems.innerHTML = '';
            mobileCartItems.innerHTML = '';

            // Если корзина пуста
            if (cart.length === 0) {
                cartItems.innerHTML = '<p class="text-gray-500 text-center py-4">Корзина пуста</p>';
                mobileCartItems.innerHTML = '<p class="text-gray-500 text-center py-4">Корзина пуста</p>';
                checkoutBtn.disabled = true;
                mobileCheckoutBtn.disabled = true;
            } else {
                // Добавление товаров в корзину
                cart.forEach(item => {
                    // Десктопная версия
                    const cartItem = document.createElement('div');
                    cartItem.className = 'cart-item flex items-center space-x-3 border-b pb-3';
                    cartItem.innerHTML = `
                        <img src="${item.image}" alt="${item.name}" class="w-16 h-16 object-cover rounded">
                        <div class="flex-grow">
                            <h4 class="font-medium">${item.name}</h4>
                            <p class="text-gray-500 text-sm">${item.articul}</p>
                            <div class="flex items-center mt-1">
                                <button class="quantity-btn decrease bg-gray-200 px-2 py-1 rounded-l" data-id="${item.id}">-</button>
                                <span class="bg-gray-100 px-3 py-1">${item.quantity}</span>
                                <button class="quantity-btn increase bg-gray-200 px-2 py-1 rounded-r" data-id="${item.id}">+</button>
                                <span class="ml-auto font-medium">${(item.price * item.quantity).toLocaleString()} ₽</span>
                            </div>
                        </div>
                        <button class="remove-item text-gray-400 hover:text-red-500" data-id="${item.id}">
                            <i class="fas fa-trash"></i>
                        </button>
                    `;
                    cartItems.appendChild(cartItem);

                    // Мобильная версия
                    const mobileCartItem = cartItem.cloneNode(true);
                    mobileCartItems.appendChild(mobileCartItem);
                });

                // Включение кнопки оформления заказа
                checkoutBtn.disabled = false;
                mobileCheckoutBtn.disabled = false;

                // Добавление обработчиков для кнопок в корзине
                const addCartEventListeners = () => {
                    document.querySelectorAll('.quantity-btn.decrease').forEach(btn => {
                        btn.removeEventListener('click', handleDecrease); // Удаляем старые обработчики
                        btn.addEventListener('click', handleDecrease);
                    });

                    document.querySelectorAll('.quantity-btn.increase').forEach(btn => {
                        btn.removeEventListener('click', handleIncrease); // Удаляем старые обработчики
                        btn.addEventListener('click', handleIncrease);
                    });

                    document.querySelectorAll('.remove-item').forEach(btn => {
                        btn.removeEventListener('click', handleRemove); // Удаляем старые обработчики
                        btn.addEventListener('click', handleRemove);
                    });
                };

                const handleDecrease = function() {
                    const productId = parseInt(this.getAttribute('data-id'));
                    const item = cart.find(item => item.id === productId);
                    if (item) updateQuantity(productId, item.quantity - 1);
                };

                const handleIncrease = function() {
                    const productId = parseInt(this.getAttribute('data-id'));
                    const item = cart.find(item => item.id === productId);
                    if (item) updateQuantity(productId, item.quantity + 1);
                };

                const handleRemove = function() {
                    const productId = parseInt(this.getAttribute('data-id'));
                    removeFromCart(productId);
                };

                addCartEventListeners();
            }

            // Расчет общей суммы
            const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
            cartTotal.textContent = `${total.toLocaleString()} ₽`;
            mobileCartTotal.textContent = `${total.toLocaleString()} ₽`;
        }

        // Инициализация
        document.addEventListener('DOMContentLoaded', function() {
            renderProducts();
            updateCart();

            // Открытие/закрытие корзины (десктоп)
            document.getElementById('cart-icon').addEventListener('click', function(e) {
                e.preventDefault();
                if (window.innerWidth >= 768) {
                    const cartPanel = document.getElementById('cart-panel');
                    cartPanel.classList.toggle('translate-x-full');
                } else {
                    document.getElementById('mobile-cart').classList.remove('hidden');
                }
            });

            document.getElementById('close-cart').addEventListener('click', function() {
                document.getElementById('cart-panel').classList.add('translate-x-full');
            });

            // Закрытие мобильной корзины
            document.getElementById('close-mobile-cart').addEventListener('click', function() {
                document.getElementById('mobile-cart').classList.add('hidden');
            });

            // Оформление заказа
            document.getElementById('checkout-btn').addEventListener('click', function() {
                if (cart.length > 0) {
                    document.getElementById('order-success').classList.remove('hidden');
                    document.getElementById('cart-panel').classList.add('translate-x-full');
                    cart = [];
                    updateCart();
                }
            });

            document.getElementById('mobile-checkout-btn').addEventListener('click', function() {
                if (cart.length > 0) {
                    document.getElementById('order-success').classList.remove('hidden');
                    document.getElementById('mobile-cart').classList.add('hidden');
                    cart = [];
                    updateCart();
                }
            });

            // Закрытие модального окна успешного заказа
            document.getElementById('close-success').addEventListener('click', function() {
                document.getElementById('order-success').classList.add('hidden');
            });

            // Обработчик изменения размера окна
            window.addEventListener('resize', function() {
                if (window.innerWidth >= 768) {
                    document.getElementById('mobile-cart').classList.add('hidden');
                } else {
                    document.getElementById('cart-panel').classList.add('translate-x-full');
                }
            });
        });
    </script>
</body>
</html>
M3
На сайте с 09.02.2022
Offline
84
#5
бтв, простенькая нейронка на 70G в 128 рам локально помещается)
ArbNet
На сайте с 27.10.2019
Offline
138
#6
master32 #:
это прям супер результат в кубе (для меня)

А по мне это шлак..

M3
На сайте с 09.02.2022
Offline
84
#7
ArbNet #:

А по мне это шлак..

ну правильно, ты же не разработчик, а студент, только учишься)

S3
На сайте с 29.03.2012
Offline
342
#8
ArbNet :
Теперь вопрос к разработчикам кто может так сделать. У меня два варианта

Если ты хочешь реальных советов, для начала показал бы что у тебя уже есть и работает. Например в виде видео демо, как происходит добавление и как  потом рендеринг.

Вообще не понимаю сути проблемы. записывай конфигурацию для каждого элемента в  json или yml файле. Только продумай, что тебе нужно хранить по каждому элементу. Что то вроде типа элемента, родителя-потомка, координаты итд.
Потом твой рендер будет просто проходится по элементам файла-конфигуратора и писать уже html код. ПРи этом остается гибкост, если ты захочешь вернуться к редактированию - не нужно будет думать, откуда брать - загрузил  конфигуратор и он тебе подкинул все необходимые элеиенты. нужен новый - выбрал элеимнт в конструкторе и он добавилс я в кофигуратор страницы со всеми нужными параметрами. я себе написал аналогичный инструмент для работы с документами когда-то давно. Такой же принцип. Только мне достаточно хранить координаты и тип энлнмнта, тебе нужно еще и вид блока и его родительский элемент.

Такой конструктор на самом деле уже вчерашний день, не будет востребован для создания сайтов. Лет 10-15 назад - наверное. Ты сейчас пытаешься  написать гибрид адоба, корелаб фигмы и тильды какой-нибудь. Неактуально.

ArbNet #:
А по мне это шлак..

А в чем шлак? легко читаемый, структурированный код,  который делает ровното, что от него надо. 
покажешь код, который твой фф  напишет для такой задачи?

ArbNet
На сайте с 27.10.2019
Offline
138
#9
ща по делам сбегаю, приду запишу видео, а то действительно вам просто словами фиг объяснишь..
S3
На сайте с 29.03.2012
Offline
342
#10
ArbNet #:
приду запишу видео

Давай, ждем, если тебе действительно нужен нормальный совет и тема не для очередного срача. Задача не уникальная, но в принципе для обучения и понятия принципов  - сойдет. Как я вижу, у тебя пока больше проблема в архитектуре, ты уперся в нее а не в сам код.

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий