Создание аккордеона с помощью Vue.js: пошаговое руководство

Создание аккордеона с помощью Vue.js: пошаговое руководство

23 февраля 2023 г.

Аккордеон

В этой статье мы будем создавать аккордеон с помощью vue.js. Аккордеон используется на веб-сайтах в основном для разделов часто задаваемых вопросов, где раздел ответов расширяется или свертывается, когда пользователь нажимает на сам вопрос или любой значок, такой как шеврон🔰, стрелка➡️ или знак плюса➕.

Предварительный просмотр: демонстрация

Сначала мы создадим проект Vue, используя vite. Поэтому убедитесь, что на вашем компьютере уже установлены vite и node. После установки vite введите следующую команду в разделе командной строки или терминале и нажмите Enter:

    npm init vite <project-name>

После нажатия Enter вам будет предложено задать несколько вопросов, например, с какой структурой вы хотите использовать vite. Мы выберем vue. Вам также будет предложено выбрать между JavaScript и TypeScript. Мы выберем JavaScript.

После ответов на вышеуказанные вопросы vite создаст для нас проект vue. После этого нам нужно выполнить несколько команд в терминале:

    cd <project-name>
    npm install // It will install all the necessary dependencies for our project in node_modules folder 

Мы будем использовать LESS в качестве препроцессора css. Итак, давайте установим его.

    npm install -D less // installs less as dev-dependency

Как только vite создаст для нас проект, мы можем запустить его, используя следующую команду:

    npm run dev

Приведенная выше команда запустит сервер разработки, созданный vite, по адресу http://localhost:5173/, и вы увидите страницу приветствия по этому адресу.

Теперь давайте изменим проект в соответствии с нашими потребностями. Сначала удалите компонент HelloWorld из папки src/components, затем шаблонный код внутри файла src/App.vue, а также удалите style.css. из папки src.

Поскольку мы используем LESS в качестве css-препроцессора, создайте папку less и внутри папки создайте файл global.less и добавьте следующее код к нему:

    *{
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }

    body{
        font-family: 'Source Sans Pro', sans-serif;
        background-color: antiquewhite;
    }

В приведенном выше коде мы сбрасываем стили браузера по умолчанию и добавляем некоторые общие стили в body.

Откройте src/main.js, и вы увидите что-то вроде этого:

    import { createApp } from 'vue'
    import './style.css'
    import App from './App.vue'

    createApp(App).mount('#app')

Замените импорт ./style.css на ./less/global.less, потому что мы удалили файл style.css и создали less/ файл global.less. После замены main.js будет выглядеть так:

    import { createApp } from 'vue'
    import './less/global.less'
    import App from './App.vue'

    createApp(App).mount('#app')

Создание аккордеона

Сначала мы создадим компонент Accordion.vue в папке src/components и импортируем этот компонент в наш компонент App.vue.< /p>

Accordion.vue👇🏻

    <script setup></script>
    <template>
        <h1>Accordion</h1>
    </template>
    <style scoped lang="less"></style>

App.vue👇🏻

    <script setup>
        /* importing Accordion.vue👇🏻 */
        import Accordion from './components/Accordion'
    </script>
    <template>
        <Accordion/> <!-- 👈🏻Using Accordion.vue -->
    </template>

App.vue — это родительский компонент всех остальных компонентов нашего приложения. Этот компонент передается функции createApp, предоставляемой vue.js, которая переводит наш код vue в собственный код JavaScript, HTML и CSS. Затем переведенный код передается функции mount, которая помещает этот переведенный код в элемент html с идентификатором app внутри файла index.html, который затем выполняется браузером. Важно импортировать другие компоненты для App.vue, чтобы vue могла переводить код во все наши компоненты.

В папке src/components мы создали компонент Accordion.vue, в котором находятся все остальные компоненты нашего приложения. В настоящее время этот компонент содержит только тег h1 с текстом «Аккордеон». Итак, если мы запустим npm run dev в терминале и откроем браузер, мы увидим текст «Accordion» на нашем экране.

Теперь давайте создадим основной контейнер для нашего аккордеона, и этот контейнер будет просто элементом html main. А также придать ему стиль. Внутри main мы создадим div с классом faqs, который будет содержать все наши элементы аккордеона.

    <script setup></script>
        <template>
            <main>  <!-- 👈🏻Main container for accordion -->
                <div class="faqs"></div>
            </main>
        </template>
    <style scoped lang="less">
        .main{
            width: 500px;
            box-shadow: 5px 5px 20px 0 rgba(0, 0, 0, 0.5);
            margin: 0 auto;
            margin-top: 10%;
            margin-bottom: 10%;
            background-color: rgb(240, 248, 255);
            padding: 10px;
            border-radius: 6px;
            .faqs{
                display: flex;
                flex-direction: column;
                gap: 20px;
            }
        }
    </style>

Теперь мы создадим еще один компонент в папке src/component с именем Faq.vue, в котором мы напишем код для одного элемента аккордеона. Наш единственный элемент аккордеона будет иметь элемент div с классом faq, который содержит два элемента div с классами header и < code>ответ соответственно. В элементе header мы создадим еще два элемента div с классами question и icon. Раздел question будет содержать часто задаваемые вопросы, а раздел icon содержит изображение шеврона. А в элементе answer у нас есть элемент p, содержащий ответ на часто задаваемые вопросы.

Часто задаваемые вопросы.vue👇🏻

    <script setup></script>
    <template>
        <div class="faq">
            <div class="header">
                <div class="question">
                    <!-- contains question -->
                </div>
                <div class="icon">
                    <img src="" alt=""/>
                </div>
            </div>
            <div class="answer">
                <p> <!-- contains answer --> </p>
            </div>
        </div>
    </template>
    <style scoped lang="less"></style>

Всего у нашего аккордеона будет пять вопросов, и мы будем хранить их в массиве объектов. Каждый объект будет иметь четыре свойства:

* id уникальный номер для идентификации объекта. * question хранит часто задаваемые вопросы. * answer хранит ответы на часто задаваемые вопросы. * isOpen хранит логическое значение, указывающее, развернута или свернута часть ответа аккордеона.

    const data = [
        {
            "id": 1,
            "question": "What is the capital of Australia?",
            "answer": "The capital of Australia is Canberra. It is a relatively new city, established in 1913, and is located between Sydney and Melbourne. Canberra is home to numerous national institutions and landmarks, including Parliament House, the Australian War Memorial, and the National Gallery of Australia. The city is known for its modern architecture and urban planning, and has a population of over 400,000 people. Despite not being one of the country's largest cities, Canberra is an important political and cultural center, and has a significant impact on the nation's economy and development.",
            "isOpen": false
        },
        {
            "id": 2,
            "question": "What is the tallest animal on earth?",
            "answer": "The tallest animal on earth is the giraffe, which can grow up to 18 feet tall. Giraffes are known for their long necks, which can reach up to 6 feet in length, and are used to reach leaves and fruits from tall trees. Giraffes are found in savannas and grasslands in Africa, and are herbivorous, feeding on leaves, fruits, and flowers. Despite their size, giraffes are social animals and live in groups called towers. They are also known for their distinctive spotted coat, which helps them blend in with their environment and avoid predators.",
            "isOpen": false
        },
        {
            "id": 3,
            "question": "What is the largest country in the world by area?",
            "answer": "The largest country in the world by area is Russia, which covers over 17 million square kilometers. Russia is located in northern Eurasia, and is bordered by Norway, Finland, Estonia, Latvia, Lithuania, Poland, Belarus, Ukraine, Georgia, Azerbaijan, Kazakhstan, China, North Korea, and Mongolia. Russia has a population of over 144 million people, and is known for its rich history and culture, as well as its natural resources, such as oil, gas, and minerals. The country is also home to numerous landmarks and tourist attractions, including the Red Square, the Kremlin, and the Hermitage Museum.",
            "isOpen": false
        },
        {
            "id": 4,
            "question": "What is the largest animal on earth?",
            "answer": "The largest animal on earth is the blue whale, which can grow up to 100 feet in length and weigh up to 200 tons. Blue whales are found in all the world's oceans, and are known for their distinctive blue-gray coloration and long, slender bodies. They are filter feeders, feeding on tiny shrimp-like creatures called krill, and can consume up to 4 tons of krill in a single day. Despite their enormous size, blue whales are graceful swimmers and can travel at speeds of up to 30 miles per hour.",
            "isOpen": false
        },
        {
            "id": 5,
            "question": "What is the capital of France?",
            "answer": "The capital of France is Paris. It is one of the most famous cities in the world, known for its beautiful architecture, rich history, and world-class museums and landmarks. Paris is home to iconic landmarks such as the Eiffel Tower, the Louvre Museum, and Notre-Dame Cathedral, and is famous for its cuisine and fashion. The city has a population of over 2 million people, and is a global center for art, fashion, and culture. Despite its cosmopolitan character, Paris has retained much of its historic charm, with narrow streets",
            "isOpen": false
        }
  ]

В Accordion.vue мы сохраним приведенный выше массив как реактивное состояние, используя ref, поэтому импортируем ref из vue. В настоящее время наши вопросы и ответы находятся в массиве, но чтобы отобразить их в браузере, мы должны извлечь их из массивов и поместить в элементы HTML. Мы уже создали компонент для Faq.vue, так что импортируйте его. Faq.vue — это компонент для одного аккордеона или одного вопроса & ответ так, мы должны передать один объект из массива данных в компонент за раз. Всего у нас пять вопросов & ответы, что означает, что мы должны вызвать Faq.vue пять раз и передать один объект question & каждый раз отвечать. Пять раз вызывать Faq.vue утомительно, поэтому мы будем использовать директиву v-for в Faq.vue для циклического перебора массива данных и на каждой итерации. , мы передадим компоненту один объект faq.

    <script setup>
        import { ref } from 'vue'
        import Faq from './Faq'

        const faqs = ref(data) // data is above array of faq questions & answers
    </script>
        <template>
            <main>  <!-- 👈🏻Main container for accordion -->
                <div class="faqs">
                    <!-- Looping over faqs array and passing single faq as a prop -->
                    <Faq
                        v-for="faq in faqs"
                        :key="faq.id"
                        :faq="faq"
                    />
                </div>
            </main>
        </template>
    <style scoped lang="less">
        .main{
            width: 500px;
            box-shadow: 5px 5px 20px 0 rgba(0, 0, 0, 0.5);
            margin: 0 auto;
            margin-top: 10%;
            margin-bottom: 10%;
            background-color: rgb(240, 248, 255);
            padding: 10px;
            border-radius: 6px;
            .faqs{
                display: flex;
                flex-direction: column;
                gap: 20px;
            }
        }
    </style>

После передачи faq в качестве реквизита мы должны получить его внутри Faq.vue с помощью макроса defineProps. Затем мы можем использовать объект faq в template файла Faq.vue для извлечения вопроса & ответ из него. Нам также нужен значок шеврона в каждом аккордеоне, поэтому давайте импортируем его и привяжем к нему атрибут src элемента img.

В конце давайте добавим стили к компоненту.

    <script setup>
        import chevron from '../assets/chevron.svg'
        defineProps(['faq']) // recieving faq object as a prop.
    </script>
    <template>
        <div class="faq">
            <div class="header">
                <div class="question">
                    {{ faq.question }} <!--Extracting question from faq prop -->
                </div>
                <div class="icon">
                    <img :src="chevron" alt="chevron-icon"/>
                </div>
            </div>
            <div class="answer">
                <p> {{ faq.answer }} </p> <!--Extracting answer from faq prop -->
            </div>
        </div>
    </template>
    <style scoped lang="less">
        .faq{
            flex-grow: 1;
            .header{
                display: flex;
                align-items: center;
                justify-content: space-between;
                border: 2px solid antiquewhite;
                padding: 10px;
                border-radius: 6px 6px 0 0;
                cursor: pointer;
                .question{
                    font-weight: 700;
                }
                .icon{
                    width: 30px;
                    height: 30px;
                    transition: transform .5s;
                    img{
                        width: 100%;
                        height: auto;
                    }
                    &.open{
                        transform: rotate(180deg);
                    }
                }
            }
            .answer{
                height: 0;
                overflow-y: scroll;
                line-height: 1.5;
                background-color: antiquewhite;
                transition: height .5s;
                &::-webkit-scrollbar{
                    width: 5px;
                }
                &::-webkit-scrollbar-track{
                    appearance: none;
                    background-color: transparent;
                }
                &::-webkit-scrollbar-thumb{
                    width: 5px;
                    background-color: rgb(232, 210, 182);
                    border-radius: 50px;
                }
                p{
                    padding: 10px;
                }
                &.open{
                    height: 200px;
                }
            }
        }
    </style>

По умолчанию часть каждого аккордеона должна быть свернута, и мы сделали это в css, задав элементу .answer height значение 0 и установив overflow в scroll, чтобы у нас была полоса прокрутки на .answer, если содержимое длинное и оно переполняется. Мы также придали стили полосе прокрутки.

Добавление функциональности

Всякий раз, когда пользователь нажимает на вопрос или значок шеврона, он должен переключать часть ответа этого аккордеона, что означает, что если часть ответа свернута, она должна расширяться, и наоборот. Следует иметь в виду, что событие щелчка произойдет в компоненте Faq.vue, но данные, которые необходимо обновить, находятся в Accordion.vue. Потому что Faq .vue является дочерним компонентом Accordion.vue, мы можем генерировать событие из Faq.vue, а затем прослушивать это событие генерирования в Accordion.vue .

Другое дело, что мы должны отслеживать, свернута или развернута часть ответа конкретного аккордеона, для этого мы будем использовать свойство isOpen объекта faq в массиве данных.

    <script setup>
        import chevron from '../assets/chevron.svg'
        defineProps(['faq']) // recieving faq object as a prop.
        const emit = defineEmits(['toggleAnswer']) // defining events to emit
        const handleClick = id => emit('toggleAnswer', id) // emitting toggleAnswer event with id attribute.
    </script>
    <template>
        <div class="faq">
            <div class="header" @click="() => handleClick(faq.id)">
                <div class="question">
                    {{ faq.question }} <!--Extracting question from faq prop -->
                </div>
                <div :class="['icon', {open: faq.isOpen}]">
                    <img :src="chevron" alt="chevron-icon"/>
                </div>
            </div>
            <div :class="['answer', {open: faq.isOpen}]">
                <p> {{ faq.answer }} </p> <!--Extracting answer from faq prop -->
            </div>
        </div>
    </template>
    <style scoped lang="less">
        /* Styling goes here */
    </style>

Сначала в Faq.vue мы добавим прослушиватель событий щелчка к элементу header, который содержит вопрос и значок шеврона. Когда пользователь нажимает на header, он запускает функции handleClick, которые отправляют событие toggleAnswer своему родительскому компоненту, но чтобы отслеживать, какой аккордеон был нажат, мы также отправьте id этого объекта часто задаваемых вопросов. Кроме того, в шаблоне мы проверяем, является ли faq.isOpen true, и если это true, то мы добавляем open к элементу answer, чтобы мы могли расширить ответную часть аккордеона. Мы также добавляем класс open к элементу icon, который содержит значок шеврона, потому что нам нужно повернуть его, если faq.isOpen true код>.

    <script setup>
        import { ref } from 'vue'
        import Faq from './Faq'

        const faqs = ref(data) // data is above array of faq questions & answers

        const toggleAnswer = id => {
            faqs.value = faqs.value.map(faq => faq.id === id ? {...faq, isOpen: !faq.isOpen} : faq)
        }
    </script>
    <template>
        <main>  <!-- 👈🏻Main container for accordion -->
            <div class="faqs">
                <!-- Looping over faqs array and passing single faq as a prop -->
                <Faq
                    v-for="faq in faqs"
                    :key="faq.id"
                    :faq="faq"
                    @toggle-answer="toggleAnswer"
                />
            </div>
        </main>
    </template>
    <style scoped lang="less">
        /* Styling goes here */
    </style>

В Accordion.vue мы слушаем toggle-answer, который выполняет функцию toggleAnswer, которая принимает id в качестве отправляемого параметра по Faq.vue. В функции toggleAnswer мы обновляем состояние faqs, перебирая массив faqs с помощью функции map. Внутри функции map на каждой итерации мы проверяем, равен ли идентификатор текущего элемента id аккордеона, на который нажали, и возвращает ли он true это означает, что текущий элемент является аккордеоном, по которому щелкнули, поэтому мы изменяем его свойство isOpen на true, если оно равно false, и наоборот.

Еще одна вещь, которую мы хотим в нашем аккордеоне, это то, что только один элемент аккордеона должен иметь развернутую часть ответа. Таким образом, если пользователь щелкает какой-либо аккордеон, чтобы развернуть его часть ответа, части ответов других элементов аккордеона должны свернуться, если они развернуты. Для этого мы сначала должны установить свойство isOpen каждого аккордеона, чье свойство isOpen равно true, равному false, за исключением одного пользователя, который только что щелкнул, выполнив это. мы позаботимся о том, чтобы каждый второй аккордеон свернул часть ответа.

В Accordion.vue

    const toggleAnswer = id => {
        faqs.value = faqs.value.map(faq => faq.isOpen && faq.id !== id ? {...faq, isOpen: false} : faq)
        faqs.value = faqs.value.map(faq => faq.id === id ? {...faq, isOpen: !faq.isOpen} : faq)
    }

Надеюсь, вам понравилась эта статья😊.


Оригинал