Geschätzte Lesezeit: 8 - 15 Minuten (2869 Wörter)

Dieses Override basiert auf das Beispiel von Heydon Pickering für "Collapsible Sections" in seinem Blog / Buch "Inclusive Components" (https://inclusive-components.design/).

Weil ich gerne eigene Templates ohne Frameworks (Bootstrap, uikit) erstelle, versuche ich mit Joomla Core neue Gestaltungsmöglichkeiten zu erschaffen, die einfach zu erreichen sind. "Collapsible sections" oder auch "accordions" genannt kann man mit Bootstrap oder uikit erstellen. Es gibt auch eine Fülle an Erweiterungen für Joomla, die diese Funktion anbieten. In diesem Override zeige ich wie man das Blog Layout mit ein bisschen CSS und Javascript umgestalten kann, um eine F.A.Q Seite in Akkordeon-Stil zu erstellen.

Da man wahrscheinlich das Blog Layout mehrmals auf einer Seite in Verwendung hat und nicht nur für eine F.A.Q. Darstellung nutzen möchte, werden wir einen neuen Menütyp erstellen. Das heißt, wir machen ein Override vom Blog Layout inklusive die XML Datei:

blog.xml -> faq.xml

blog.php -> faq.php

blog_item.php -> faq_item.php

Fürs Erste brauchen wir blog_children.php und blog_links.php nicht.

In der XML Datei werden wir alle Parameter entfernen, die für eine F.A.Q. Liste nicht notwendig sind:

<?xml version="1.0" encoding="utf-8"?>
<metadata>
	<layout title="FAQ Layout" option="FAQ Layout">
		<help key = "JHELP_MENUS_MENU_ITEM_ARTICLE_CATEGORY_BLOG" />
		<message>
			<![CDATA[Verzeichnis mit Filter+]]>
		</message>
	</layout>

	<!-- Add fields to the request variables for the layout. -->
	<fields name="request">
		<fieldset name="request"
			addfieldpath="/administrator/components/com_categories/models/fields"
		>
			<field
				name="id"
				type="modal_category"
				label="JGLOBAL_CHOOSE_CATEGORY_LABEL"
				description="JGLOBAL_CHOOSE_CATEGORY_DESC"
				extension="com_content"
				required="true"
				select="true"
				new="true"
				edit="true"
				clear="true"
			/>

			<field
				name="filter_tag"
				type="tag"
				label="JTAG"
				description="JTAG_FIELD_SELECT_DESC"
				multiple="true"
				mode="nested"
			/>
		</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="faq"
				/>

				<field
					name="show_category_title"
					type="list"
					label="JGLOBAL_SHOW_CATEGORY_TITLE"
					description="JGLOBAL_SHOW_CATEGORY_TITLE_DESC"
					useglobal="true"
					class="chzn-color"
					>
					<option value="0">JHIDE</option>
					<option value="1">JSHOW</option>
				</field>

				<field
					name="show_description"
					type="list"
					label="JGLOBAL_SHOW_CATEGORY_DESCRIPTION_LABEL"
					description="JGLOBAL_SHOW_CATEGORY_DESCRIPTION_DESC"
					useglobal="true"
					class="chzn-color"
					>
					<option value="0">JHIDE</option>
					<option value="1">JSHOW</option>
				</field>

				<field
					name="show_description_image"
					type="list"
					label="JGLOBAL_SHOW_CATEGORY_IMAGE_LABEL"
					description="JGLOBAL_SHOW_CATEGORY_IMAGE_DESC"
					useglobal="true"
					class="chzn-color"
					>
					<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"
					description="COM_CONTENT_FIELD_SHOW_CAT_TAGS_DESC"
					useglobal="true"
					class="chzn-color"
					>
					<option value="0">JHIDE</option>
					<option value="1">JSHOW</option>
				</field>

				<field
					name="page_subheading"
					type="text"
					label="JGLOBAL_SUBHEADING_LABEL"
					description="JGLOBAL_SUBHEADING_DESC"
					size="20"
				/>
		</fieldset>

		<fieldset name="advanced" label="JGLOBAL_BLOG_LAYOUT_OPTIONS">
				<field
					name="bloglayout"
					type="spacer"
					label="JGLOBAL_SUBSLIDER_BLOG_LAYOUT_LABEL"
					class="text"
				/>

				<field
					name="num_intro_articles"
					type="number"
					label="JGLOBAL_NUM_INTRO_ARTICLES_LABEL"
					description="JGLOBAL_NUM_INTRO_ARTICLES_DESC"
					useglobal="true"
					size="3"
				/>

				<field
					name="spacer1"
					type="spacer"
					hr="true"
				/>

				<field
					name="orderby_sec"
					type="list"
					label="JGLOBAL_ARTICLE_ORDER_LABEL"
					description="JGLOBAL_ARTICLE_ORDER_DESC"
					useglobal="true"
					>
					<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">JGLOBAL_MOST_HITS</option>
					<option value="rhits">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"
					description="JGLOBAL_ORDERING_DATE_DESC"
					useglobal="true"
					>
					<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"
					description="JGLOBAL_PAGINATION_DESC"
					useglobal="true"
					class="chzn-color"
					>
					<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"
					description="JGLOBAL_PAGINATION_RESULTS_DESC"
					useglobal="true"
					class="chzn-color"
					>
					<option value="0">JHIDE</option>
					<option value="1">JSHOW</option>
				</field>

				<field
					name="show_featured"
					type="list"
					default=""
					label="JGLOBAL_SHOW_FEATURED_ARTICLES_LABEL"
					description="JGLOBAL_SHOW_FEATURED_ARTICLES_DESC"
					useglobal="true"
					class="chzn-color"
					>
					<option value="show">JSHOW</option>
					<option value="hide">JHIDE</option>
					<option value="only">JONLY</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"
				description="JGLOBAL_SHOW_FEED_LINK_DESC"
				useglobal="true"
				class="chzn-color"
				>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>

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

 

Die faq.php Datei werden wir auch sehr vereinfachen und lassen nur den Block für die Intro Items übrig:

<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_content
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

JHtml::addIncludePath(JPATH_COMPONENT . '/helpers');

JHtml::_('behavior.caption');

$dispatcher = JEventDispatcher::getInstance();

$this->category->text = $this->category->description;
$dispatcher->trigger('onContentPrepare', array($this->category->extension . '.categories', &$this->category, &$this->params, 0));
$this->category->description = $this->category->text;

$results = $dispatcher->trigger('onContentAfterTitle', array($this->category->extension . '.categories', &$this->category, &$this->params, 0));
$afterDisplayTitle = trim(implode("\n", $results));

$results = $dispatcher->trigger('onContentBeforeDisplay', array($this->category->extension . '.categories', &$this->category, &$this->params, 0));
$beforeDisplayContent = trim(implode("\n", $results));

$results = $dispatcher->trigger('onContentAfterDisplay', array($this->category->extension . '.categories', &$this->category, &$this->params, 0));
$afterDisplayContent = trim(implode("\n", $results));

?>
<div class="blog<?php echo $this->pageclass_sfx; ?>" itemscope itemtype="https://schema.org/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) or $this->params->get('page_subheading')) : ?>
		<h2> <?php echo $this->escape($this->params->get('page_subheading')); ?>
			<?php if ($this->params->get('show_category_title')) : ?>
				<span class="subheading-category"><?php echo $this->category->title; ?></span>
			<?php endif; ?>
		</h2>
	<?php endif; ?>
	<?php echo $afterDisplayTitle; ?>

	<?php if ($this->params->get('show_cat_tags', 1) && !empty($this->category->tags->itemTags)) : ?>
		<?php $this->category->tagLayout = new JLayoutFile('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')) : ?>
				<img src="/<?php echo $this->category->getParams()->get('image'); ?>" alt="<?php echo htmlspecialchars($this->category->getParams()->get('image_alt'), ENT_COMPAT, 'UTF-8'); ?>"/>
			<?php endif; ?>
			<?php echo $beforeDisplayContent; ?>
			<?php if ($this->params->get('show_description') && $this->category->description) : ?>
				<?php echo JHtml::_('content.prepare', $this->category->description, '', 'com_content.category'); ?>
			<?php endif; ?>
			<?php echo $afterDisplayContent; ?>
		</div>
	<?php endif; ?>

	<?php if (!empty($this->intro_items)) : ?>
		<?php foreach ($this->intro_items as $key => &$item) : ?>
			<?php
				$this->item = &$item;
				echo $this->loadTemplate('item');
			?>
		<?php endforeach; ?>
	<?php endif; ?>

	<?php if (($this->params->def('show_pagination', 1) == 1 || ($this->params->get('show_pagination') == 2)) && ($this->pagination->get('pages.total') > 1)) : ?>
		<div class="pagination">
			<?php if ($this->params->def('show_pagination_results', 1)) : ?>
				<p class="counter pull-right"> <?php echo $this->pagination->getPagesCounter(); ?> </p>
			<?php endif; ?>
			<?php echo $this->pagination->getPagesLinks(); ?> </div>
	<?php endif; ?>
</div>

 

Für mein Beispiel ist die Darstellung der Fragen / Antworten minimalistisch: Der Titel der Beiträge ist die Frage, der Inhalt ist die Antwort. Deswegen ist die faq_item.php Datei auch minimalistisch gehalten:

<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_content
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

// Create a shortcut for params.
$params = $this->item->params;
JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
$canEdit = $this->item->params->get('access-edit');
$info    = $params->get('info_block_position', 0);

// Check if associations are implemented. If they are, define the parameter.
$assocParam = (JLanguageAssociations::isEnabled() && $params->get('show_associations'));

?>
<div class="faq">
	<?php if ($this->item->state == 0 || strtotime($this->item->publish_up) > strtotime(JFactory::getDate())
		|| ((strtotime($this->item->publish_down) < strtotime(JFactory::getDate())) && $this->item->publish_down != JFactory::getDbo()->getNullDate())) : ?>
	<div class="system-unpublished">
	<?php endif; ?>

	<?php echo JLayoutHelper::render('joomla.content.faq_style_default_item_title', $this->item); ?>

	<div hidden="">
		<?php if (!$params->get('show_intro')) : ?>
			<?php // Content is generated by content plugin event "onContentAfterTitle" ?>
			<?php echo $this->item->event->afterDisplayTitle; ?>
		<?php endif; ?>

		<?php // Content is generated by content plugin event "onContentBeforeDisplay" ?>
		<?php echo $this->item->event->beforeDisplayContent; ?>

		<?php echo $this->item->introtext; ?>

	</div>
	<?php if ($this->item->state == 0 || strtotime($this->item->publish_up) > strtotime(JFactory::getDate())
		|| ((strtotime($this->item->publish_down) < strtotime(JFactory::getDate())) && $this->item->publish_down != JFactory::getDbo()->getNullDate())) : ?>
	</div>
	<?php endif; ?>

	<?php // Content is generated by content plugin event "onContentAfterDisplay" ?>
	<?php echo $this->item->event->afterDisplayContent; ?>
</div>

 

Ich habe ein div Container um den Inhalt des Beitrags ergänzt mit dem Attribut "hidden", das sorgt dafür, dass die Antwort erstmal versteckt bleibt.

Für den Titel des Beitrags habe ich ein eigenes Layout unter html/layouts/joomla/content angelegt: faq_style_default_item_title.php:

<?php
/**
 * @package     Joomla.Site
 * @subpackage  Layout
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

// Create a shortcut for params.
$params = $displayData->params;
$canEdit = $displayData->params->get('access-edit');
JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html');
?>

<?php if ($displayData->state == 0 || $params->get('show_title') || ($params->get('show_author') && !empty($displayData->author ))) : ?>

	<h2 itemprop="name" class="panel-title">
		<button aria-expanded="false"><?php echo $this->escape($displayData->title); ?> <i class="panel-icon"></i> </button>
	</h2>
	
	<?php if ($displayData->state == 0) : ?>
		<span class="label label-warning"><?php echo JText::_('JUNPUBLISHED'); ?></span>
	<?php endif; ?>

	<?php if (strtotime($displayData->publish_up) > strtotime(JFactory::getDate())) : ?>
		<span class="label label-warning"><?php echo JText::_('JNOTPUBLISHEDYET'); ?></span>
	<?php endif; ?>
	
	<?php if ($displayData->publish_down != JFactory::getDbo()->getNullDate()
		&& (strtotime($displayData->publish_down) < strtotime(JFactory::getDate()))
	) : ?>
		<span class="label label-warning"><?php echo JText::_('JEXPIRED'); ?></span>
	<?php endif; ?>
<?php endif; ?>

 

Der h2 Titel bekommt die Klasse "panel-title" und darunter einen Button mit einem Icon. Der Button hat das Attribut aria-expanded="false", das heißt die Fragen sind standarmäßig geschlossen. Diese Elemente werden wir mit CSS gestalten und mit Javascript einer Funktionalität verleihen.

 

Jetzt brauchen wir eine Kategorie "F.A.Q." mit den passenden Beiträgen. Dann erstellen wir den Menüpunkt und machen uns an die Gestaltung mit CSS:

/* Collapse */
.faq {
	margin-bottom: var(--s1);
}
.panel-title {
  border-bottom: 2px solid;
  cursor:pointer;
}
.panel-title button {
  all: inherit;
  border: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  padding: 0.5em 0;
}
.panel-title button:focus .panel-icon {
	outline: 2px solid var(--color-contrast);
	outline-offset: 4px;
}
.panel-icon {
	width: 60px;
	height: 60px;
	text-align: center;
}
.panel-icon::before {
	content: "+";
}
.panel-title [aria-expanded="true"] .panel-icon::before {
	content: "-";
}

 

Der Titel des Beitrags (Frage) bekommt einen Border unten, das dient der visuellen Trennung der Fragen. Den cursor: pointer; signalisiert, dass man darauf klicken kann.

Das Button Element wird als "flex" dargestellt, damit Titel und Icon in einer Linie angezeigt werden.

Das Icon ist ein "+" wenn die Frage noch geschlossen ist und wird zu "-" wenn die Frage offen ist (das stellen wir fest, indem wir das Attribut [aria-expanded] vom Titel abfragen.

Noch tut sich nicht viel bei unserer F.A.Q. Liste. Das ändern wir mit Javascript. Damit wir die Fragen / Antworten öffnen und schließen können, brauchen wir eine Funktion:

document.addEventListener("DOMContentLoaded", function(){
    (function () {
      const headings = document.querySelectorAll('.panel-title');

      Array.prototype.forEach.call(headings, h => {
        let btn = h.querySelector('button');
        let target = h.nextElementSibling;

        btn.onclick = () => {
          let expanded = btn.getAttribute('aria-expanded') === 'true';

          btn.setAttribute('aria-expanded', !expanded);
          target.hidden = expanded;
        }
      });
    })();
});

Damit schauen wir nach allen Elementen mit der Klasse "panel-title" (das sind unsere Fragen) und den darin vorhandene "button". Wenn man auf "button" klickt (btn.onclick) wird das Attribut "aria-expanded" auf "true" gesetzt und das "target" Element (der Inhalt des Beitrags = Antwort) bekommt das "hidden" weg und wird sichtbar.

Das Ergebnis sieht dann so aus:

faq

 

Und hier "live" auf der Beispiel Seite: https://www.dr-menzel-it.de/overrides/f-a-q

 

Ich hoffe, ich konnte wieder zeigen, dass man mit wenig Code praktische Darstellungen mit Joomla Core erstellen kann.

 

Hier geht es zu Daniels Lösung