阅读时间10分钟 (1997字)

使用Bootstrap实现可折叠部分

2024---JCM-collapsible-bootstrap

你是否有一些内容很多或者想要创建一个让访客自己决定他们想看到哪些信息的页面(如FAQ页面)?手风琴式布局就非常合适。访客可以查看主题概览,点击感兴趣的,内容就会展开。而且好的是:你不需要扩展来实现这个功能,只需要稍微调整一下Joomla的核心视图即可。以下是实现方法!

之前的文章(见案例研究2)中,我展示了如何使用页面分隔符插件创建一个FAQ页面。这对于少量内容是有效的,但是如果你需要创建更复杂的FAQ(或其他类型的可折叠内容),最佳方法是创建一个覆盖。

在这篇文章中,我将展示如何使用Bootstrap重新设计类别博客布局,将文章显示为“可折叠部分”或称为“手风琴”。我从Bootstrap示例中改编了代码,并添加了一些额外的功能,以使手风琴更易于访问,这是Bootstrap根据BootstrapWAI的示例所推荐的。

由于你可能在页面上多次使用类别博客布局,并且不希望它们看起来像FAQ,我们将创建一个新的菜单类型来处理手风琴视图。这样我们就可以为这个特定的页面选择我们的布局。这意味着我们将覆盖博客布局,包括XML文件

blog.xml -> accordion.xml
blog.php -> accordion.php

你可以在模板文件中找到这些。转到系统 -> 网站模板,找到你的模板并点击文件链接。点击创建覆盖标签,找到com_content(在组件下),点击类别,Joomla将为你创建覆盖。你将在模板编辑器旁边的侧边栏中的文件列表中找到它们,在html下。

将有几份您不需要的文件,我已经为这个教程移除了它们:blog-item.php,blog-children.php 和 blog-links.php。

Accordion.xml,我们的 blog.xml 覆盖文件

在 XML 文件中,我们将移除一些不适用于常见问题解答的参数,并添加一个选项来设置手风琴的第一个元素是打开还是关闭

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
    <layout title="Accordion" option="COM_CONTENT_CATEGORY_VIEW_BLOG_OPTION">
        <help key = "Menu_Item:_Category_Blog" />
        <message>
            <![CDATA[Display articles from a category as accordion]]>
        </message>
    </layout>


    <!-- Add fields to the request variables for the layout. -->
    <fields name="request">
        <fieldset name="request"
            addfieldprefix="Joomla\Component\Categories\Administrator\Field"
        >
            <field
                name="id"
                type="modal_category"
                label="JGLOBAL_CHOOSE_CATEGORY_LABEL"
                extension="com_content"
                required="true"
                select="true"
                new="true"
                edit="true"
                clear="true"
            />


            <field
                name="filter_tag"
                type="tag"
                label="JTAG"
                multiple="true"
                mode="nested"
                custom="deny"
            />
        </fieldset>
    </fields>


    <!-- Add fields to the parameters object for the layout. -->
    <fields name="params">
        <fieldset name="basic" label="JGLOBAL_CATEGORY_OPTIONS">
                <field
                    name="layout_type"
                    type="hidden"
                    default="blog"
                />


                <field
                    name="show_category_title"
                    type="list"
                    label="JGLOBAL_SHOW_CATEGORY_TITLE"
                    useglobal="true"
                    class="form-select-color-state"
                    validate="options"
                    >
                    <option value="0">JHIDE</option>
                    <option value="1">JSHOW</option>
                </field>


                <field
                    name="show_description"
                    type="list"
                    label="JGLOBAL_SHOW_CATEGORY_DESCRIPTION_LABEL"
                    useglobal="true"
                    class="form-select-color-state"
                    validate="options"
                    >
                    <option value="0">JHIDE</option>
                    <option value="1">JSHOW</option>
                </field>


                <field
                    name="show_description_image"
                    type="list"
                    label="JGLOBAL_SHOW_CATEGORY_IMAGE_LABEL"
                    useglobal="true"
                    class="form-select-color-state"
                    validate="options"
                    >
                    <option value="0">JHIDE</option>
                    <option value="1">JSHOW</option>
                </field>


                <field
                    name="maxLevel"
                    type="list"
                    label="JGLOBAL_MAXLEVEL_LABEL"
                    description="JGLOBAL_MAXLEVEL_DESC"
                    useglobal="true"
                    validate="options"
                    >
                    <option value="-1">JALL</option>
                    <option value="0">JNONE</option>
                    <option value="1">J1</option>
                    <option value="2">J2</option>
                    <option value="3">J3</option>
                    <option value="4">J4</option>
                    <option value="5">J5</option>
                </field>


                <field
                    name="show_empty_categories"
                    type="list"
                    label="JGLOBAL_SHOW_EMPTY_CATEGORIES_LABEL"
                    useglobal="true"
                    class="form-select-color-state"
                    validate="options"
                    >
                    <option value="0">JHIDE</option>
                    <option value="1">JSHOW</option>
                </field>


                <field
                    name="show_no_articles"
                    type="list"
                    label="COM_CONTENT_NO_ARTICLES_LABEL"
                    useglobal="true"
                    class="form-select-color-state"
                    validate="options"
                    >
                    <option value="0">JHIDE</option>
                    <option value="1">JSHOW</option>
                </field>


                <field
                    name="show_cat_tags"
                    type="list"
                    label="COM_CONTENT_FIELD_SHOW_CAT_TAGS_LABEL"
                    useglobal="true"
                    class="form-select-color-state"
                    validate="options"
                    >
                    <option value="0">JHIDE</option>
                    <option value="1">JSHOW</option>
                </field>


        </fieldset>


        <fieldset name="advanced" label="JGLOBAL_BLOG_LAYOUT_OPTIONS" description="JGLOBAL_SUBSLIDER_BLOG_LAYOUT_LABEL">


            <field
                name="num_leading_articles"
                type="hidden"
                default="0"
            />


            <field
                name="num_intro_articles"
                type="number"
                label="JGLOBAL_NUM_INTRO_ARTICLES_LABEL"
                filter="integer"
                validate="number"
                min="0"
                useglobal="true"
                parentclass="stack span-1"
            />


            <field
                name="collapse_first_item"
                type="radio"
                layout="joomla.form.field.radio.switcher"
                label="First element open"
                parentclass="stack span-2-inline"
                validate="options"
                default="1"
                >
                <option value="0">JNO</option>
                <option value="1">JYES</option>
            </field>


            <field
                name="show_featured"
                type="list"
                label="JGLOBAL_SHOW_FEATURED_ARTICLES_LABEL"
                default=""
                useglobal="true"
                class="form-select-color-state"
                validate="options"
                parentclass="stack span-1"
                >
                <option value="show">JSHOW</option>
                <option value="hide">JHIDE</option>
                <option value="only">JONLY</option>
            </field>


            <field
                name="show_subcategory_content"
                type="list"
                label="JGLOBAL_SHOW_SUBCATEGORY_CONTENT_LABEL"
                useglobal="true"
                validate="options"
                parentclass="stack span-1-inline"
                >
                <option value="0">JNONE</option>
                <option value="-1">JALL</option>
                <option value="1">J1</option>
                <option value="2">J2</option>
                <option value="3">J3</option>
                <option value="4">J4</option>
                <option value="5">J5</option>
            </field>
            <field
                name="orderby_pri"
                type="list"
                label="JGLOBAL_CATEGORY_ORDER_LABEL"
                useglobal="true"
                validate="options"
                parentclass="stack span-2"
                >
                <option value="none">JGLOBAL_NO_ORDER</option>
                <option value="alpha">JGLOBAL_TITLE_ALPHABETICAL</option>
                <option value="ralpha">JGLOBAL_TITLE_REVERSE_ALPHABETICAL</option>
                <option value="order">JGLOBAL_CATEGORY_MANAGER_ORDER</option>
            </field>


            <field
                name="orderby_sec"
                type="list"
                label="JGLOBAL_ARTICLE_ORDER_LABEL"
                useglobal="true"
                validate="options"
                parentclass="stack span-2-inline"
                >
                <option value="front">COM_CONTENT_FEATURED_ORDER</option>
                <option value="rdate">JGLOBAL_MOST_RECENT_FIRST</option>
                <option value="date">JGLOBAL_OLDEST_FIRST</option>
                <option value="alpha">JGLOBAL_TITLE_ALPHABETICAL</option>
                <option value="ralpha">JGLOBAL_TITLE_REVERSE_ALPHABETICAL</option>
                <option value="author">JGLOBAL_AUTHOR_ALPHABETICAL</option>
                <option value="rauthor">JGLOBAL_AUTHOR_REVERSE_ALPHABETICAL</option>
                <option value="hits" requires="hits">JGLOBAL_MOST_HITS</option>
                <option value="rhits" requires="hits">JGLOBAL_LEAST_HITS</option>
                <option value="random">JGLOBAL_RANDOM_ORDER</option>
                <option value="order">JGLOBAL_ORDERING</option>
                <option value="rorder">JGLOBAL_REVERSE_ORDERING</option>
                <option value="vote" requires="vote">JGLOBAL_VOTES_DESC</option>
                <option value="rvote" requires="vote">JGLOBAL_VOTES_ASC</option>
                <option value="rank" requires="vote">JGLOBAL_RATINGS_DESC</option>
                <option value="rrank" requires="vote">JGLOBAL_RATINGS_ASC</option>
            </field>


            <field
                name="order_date"
                type="list"
                label="JGLOBAL_ORDERING_DATE_LABEL"
                useglobal="true"
                validate="options"
                parentclass="stack span-2-inline"
                >
                <option value="created">JGLOBAL_CREATED</option>
                <option value="modified">JGLOBAL_MODIFIED</option>
                <option value="published">JPUBLISHED</option>
                <option value="unpublished">JUNPUBLISHED</option>
            </field>


            <field
                name="show_pagination"
                type="list"
                label="JGLOBAL_PAGINATION_LABEL"
                useglobal="true"
                class="form-select-color-state"
                validate="options"
                parentclass="stack span-1"
                >
                <option value="0">JHIDE</option>
                <option value="1">JSHOW</option>
                <option value="2">JGLOBAL_AUTO</option>
            </field>


            <field
                name="show_pagination_results"
                type="list"
                label="JGLOBAL_PAGINATION_RESULTS_LABEL"
                useglobal="true"
                class="form-select-color-state"
                validate="options"
                parentclass="stack span-1-inline"
                >
                <option value="0">JHIDE</option>
                <option value="1">JSHOW</option>
            </field>


        </fieldset>


        <fieldset name="integration" label="COM_MENUS_INTEGRATION_FIELDSET_LABEL">
            <field
                name="show_feed_link"
                type="list"
                label="JGLOBAL_SHOW_FEED_LINK_LABEL"
                useglobal="true"
                class="form-select-color-state"
                validate="options"
                >
                <option value="0">JHIDE</option>
                <option value="1">JSHOW</option>
            </field>


            <field
                name="feed_summary"
                type="list"
                label="JGLOBAL_FEED_SUMMARY_LABEL"
                useglobal="true"
                validate="options"
                >
                <option value="0">JGLOBAL_INTRO_TEXT</option>
                <option value="1">JGLOBAL_FULL_TEXT</option>
            </field>
        </fieldset>
    </fields>
</metadata>

Accordion.php,我们的 blog.php 覆盖文件

Accordion.php 文件也将大大简化,只留下介绍性项目的块

<?php


/**
 * @package     Joomla.Site
 * @subpackage  com_content
 *
 * @copyright   (C) 2006 Open Source Matters, Inc. <https://www.joomla.net.cn>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */


defined('_JEXEC') or die;


use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;


$app = Factory::getApplication();


$this->category->text = $this->category->description;
$app->triggerEvent('onContentPrepare', [$this->category->extension . '.categories', &$this->category, &$this->params, 0]);
$this->category->description = $this->category->text;


$results = $app->triggerEvent('onContentAfterTitle', [$this->category->extension . '.categories', &$this->category, &$this->params, 0]);
$afterDisplayTitle = trim(implode("\n", $results));


$results = $app->triggerEvent('onContentBeforeDisplay', [$this->category->extension . '.categories', &$this->category, &$this->params, 0]);
$beforeDisplayContent = trim(implode("\n", $results));


$results = $app->triggerEvent('onContentAfterDisplay', [$this->category->extension . '.categories', &$this->category, &$this->params, 0]);
$afterDisplayContent = trim(implode("\n", $results));


$htag       = $this->params->get('show_page_heading') ? 'h2' : 'h1';
$htagart    = $this->params->get('show_page_heading') && $this->params->get('show_category_title') ? 'h3' : 'h2';


HTMLHelper::_('bootstrap.collapse');
?>


<div class="com-content-category-blog blog">
    <?php if ($this->params->get('show_page_heading')) : ?>
        <div class="page-header">
            <h1> <?php echo $this->escape($this->params->get('page_heading')); ?> </h1>
        </div>
    <?php endif; ?>


    <?php if ($this->params->get('show_category_title', 1)) : ?>
    <<?php echo $htag; ?>>
        <?php echo $this->category->title; ?>
    </<?php echo $htag; ?>>
    <?php endif; ?>
    <?php echo $afterDisplayTitle; ?>


    <?php if ($this->params->get('show_cat_tags', 1) && !empty($this->category->tags->itemTags)) : ?>
        <?php $this->category->tagLayout = new FileLayout('joomla.content.tags'); ?>
        <?php echo $this->category->tagLayout->render($this->category->tags->itemTags); ?>
    <?php endif; ?>


    <?php if ($beforeDisplayContent || $afterDisplayContent || $this->params->get('show_description', 1) || $this->params->def('show_description_image', 1)) : ?>
        <div class="category-desc clearfix">
            <?php if ($this->params->get('show_description_image') && $this->category->getParams()->get('image')) : ?>
                <?php echo LayoutHelper::render(
                    'joomla.html.image',
                    [
                        'src' => $this->category->getParams()->get('image'),
                        'alt' => empty($this->category->getParams()->get('image_alt')) && empty($this->category->getParams()->get('image_alt_empty')) ? false : $this->category->getParams()->get('image_alt'),
                    ]
                ); ?>
            <?php endif; ?>
            <?php echo $beforeDisplayContent; ?>
            <?php if ($this->params->get('show_description') && $this->category->description) : ?>
                <?php echo HTMLHelper::_('content.prepare', $this->category->description, '', 'com_content.category'); ?>
            <?php endif; ?>
            <?php echo $afterDisplayContent; ?>
        </div>
    <?php endif; ?>


    <?php if (empty($this->intro_items)) : ?>
        <?php if ($this->params->get('show_no_articles', 1)) : ?>
            <div class="alert alert-info">
                <span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
                    <?php echo Text::_('COM_CONTENT_NO_ARTICLES'); ?>
            </div>
        <?php endif; ?>
    <?php endif; ?>


    <?php if (!empty($this->intro_items)) : ?>
        <?php
            $count_items = 0;
            $collapse_first_item = $this->params->get('collapse_first_item',0);
        ?>
        <div class="accordion" id="accordionBlog">
        <?php foreach ($this->intro_items as $key => &$item) : ?>
            <?php
                $canEdit = $item->params->get('access-edit');
                $count_items++;
            ?>
            <div class="accordion-item">
                <<?php echo $htagart; ?> class="accordion-header">
                    <button id="accordionItem-<?php echo $item->id; ?>" class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-<?php echo $item->id; ?>" aria-expanded="<?php echo ($count_items == 1 && $collapse_first_item == 1) ? 'true' : 'false'; ?>" aria-controls="collapse-<?php echo $item->id; ?>">
                        <?php echo $this->escape($item->title); ?>
                    </button>
                </<?php echo $htagart; ?>>
                <div id="collapse-<?php echo $item->id; ?>" class="accordion-collapse collapse <?php echo ($count_items == 1 && $collapse_first_item == 1) ? 'show' : ''; ?>" data-bs-parent="#accordionBlog" role="region" aria-labelledby="accordionItem-<?php echo $item->id; ?>">
                    <div class="accordion-body">
                        <?php if ($canEdit) : ?>
                            <?php echo LayoutHelper::render('joomla.content.icons', ['params' => $item->params, 'item' => $item]); ?>
                        <?php endif; ?>


                        <?php // Content is generated by content plugin event "onContentAfterTitle" ?>
                        <?php echo $item->event->afterDisplayTitle; ?>


                        <?php // Content is generated by content plugin event "onContentBeforeDisplay" ?>
                        <?php echo $item->event->beforeDisplayContent; ?>
                        <div class="item-content">
                            <?php echo $item->introtext; ?>
                            <?php echo $item->fulltext; ?>
                        </div>
                        <?php // Content is generated by content plugin event "onContentAfterDisplay" ?>
                        <?php echo $item->event->afterDisplayContent; ?>
                    </div>
                </div>
            </div>
        <?php endforeach; ?>
        </div>
    <?php endif; ?>


    <?php // Code to add a link to submit an article. ?>
    <?php if ($this->category->getParams()->get('access-create')) : ?>
        <?php echo HTMLHelper::_('contenticon.create', $this->category, $this->category->params); ?>
    <?php endif; ?>
    <?php if (($this->params->def('show_pagination', 1) == 1 || ($this->params->get('show_pagination') == 2)) && ($this->pagination->pagesTotal > 1)) : ?>
        <div class="com-content-category-blog__navigation w-100">
            <?php if ($this->params->def('show_pagination_results', 1)) : ?>
                <p class="com-content-category-blog__counter counter float-md-end pt-3 pe-2">
                    <?php echo $this->pagination->getPagesCounter(); ?>
                </p>
            <?php endif; ?>
            <div class="com-content-category-blog__pagination">
                <?php echo $this->pagination->getPagesLinks(); ?>
            </div>
        </div>
    <?php endif; ?>
</div>

对于我的例子,问题/答案的展示是简约的:文章的标题是问题,内容是答案。

包含所有文章的父 div 元素将获得“accordion”类(这是执行魔法的 Bootstrap 类),并且具有“accordionBlog”的 id。每一篇文章都是一个“accordion-item”,也是一个 Bootstrap 类。

文章的标题包含在一个需要多个属性的按钮元素中

  • id:元素的唯一标识符(将在以后使用)
  • class:accordion-button,Bootstrap 风格
  • data-bs-toggle:Bootstrap 属性,由 JavaScript 使用
  • data-bs-target:Bootstrap 属性,由 JavaScript 使用,值是下一个元素的 id(见下文)
  • aria-expanded:为辅助技术提供额外信息(例如屏幕阅读器)
  • aria-controls:为辅助技术提供额外信息(例如屏幕阅读器),值是下一个元素的 id(见下文)

属性“aria-expanded”的值是“false”,问题默认为关闭。您可以通过菜单项中的设置来控制是否要第一个元素打开。如果是第一个元素并且定义为打开,则“aria-expanded”的值将是“true”

<?php echo ($count_items == 1 && $collapse_first_item == 1) ? 'true' : 'false'; ?>

文章的内容(答案)位于另一个具有多个属性的 div 元素中

  • id:元素的唯一标识符,与按钮的“data-bs-target”匹配。为了使 id 唯一,我使用文章的 id:
    id="collapse-<?php echo $item->id; ?>"
  • class:accordion-collapse collapse,并且如果第一个元素应该默认打开,Bootstrap 风格
  • data-bs-parent:它指向父元素的 id(accordionBlog)
  • region:创建一个包含当前展开的手风琴面板的 landmark 区域
  • aria-labelledby:为辅助技术提供额外信息(例如屏幕阅读器),定义区域元素的可访问名称(在我们的情况下是文章的标题)

现在我们需要一个“常见问题解答”类别以及相应的帖子。然后我们创建菜单项

Screenshot of menu options

结果看起来像这样

Screenshot of the accordion

并且在这里,“实时”在 Cassiopeia 示例页面 上。

您不需要编写任何 CSS 或 JavaScript 代码。Joomla 提供的 Bootstrap 库为您完成了所有工作。
使用这种方法创建常见问题解答的另一个优点是,您可以轻松地在后端通过创建或取消发布文章来添加/删除新问题。您还可以通过更改菜单项中的设置或移动文章来更改问题的顺序。

我希望我能再次证明 Joomla 是一个非常灵活的系统,并且通过代码中的小改动,您可以创建几乎任何东西!

发表在 Joomla 社区杂志上的一些文章代表了作者对特定主题的个人观点或经验,可能并不反映 Joomla 项目官方立场

3
在内容内创建全宽Joomla模块
利用Bearsampp的强大功能:开发者指南...
 

评论

已经注册? 在此登录
尚未发表评论。成为第一个发表评论的人

通过接受,您将访问https://magazine.joomla.net.cn/外部第三方提供的服务