12

I'm trying to change the order of the tabs on the product page in Magento 2. Default is Details|More Information|Reviews.

I tried:

Vendor/theme/Magento_Catalog/layout/catalog_product_view.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <move element="product.info.description" destination="product.info.details" after="-" />
    </body>
</page>

But that doesn't work, and thats the recommended way for moving elements. I was able to move the tabs out of the tab area and into other areas, as well as add new tabs, but I cannot control the order of the tabs.

My guess is that it has something to do with group="detailed_info"; It looks like Magento grabs the layout elements with this attribute in the XML and loops through through it to create the tabs.

Is there a way to change the order of tabs without rewriting the module?

  • Did you find an answer to this? I'm having the same problem. – Alex May 5 '16 at 12:40
  • I have no answer yet, sorry. – andyjv May 6 '16 at 13:48
  • I tried using the move element and came to the same conclusion as you, they can be moved outside the tabs but not ordered within them. – Ben Crook May 26 '16 at 11:29
  • It can be done by layout only with a little trick. Shown here :magento.stackexchange.com/questions/106412/… – skymeissner Aug 21 '16 at 15:54

13 Answers 13

21

My approach is a little different but probably more future proof, in case of adding new tabs later on and changing the priority/order of these tabs.

I passed an argument for each tab via XML File in my themes XML file

...
<arguments>
    <argument name="priority" xsi:type="string">REPLACE WITH SOME NUMBER</argument>
</arguments>
...

So my themes XML file looks something like this:

<referenceBlock name="product.info.details">
        <referenceBlock name="product.info.description">
            <arguments>
                <argument name="priority" xsi:type="string">1</argument>
            </arguments>
        </referenceBlock>
        <referenceBlock name="product.attributes">
            <arguments>
                <argument name="priority" xsi:type="string">3</argument>
            </arguments>
        </referenceBlock>
        <referenceBlock name="reviews.tab">
            <arguments>
                <argument name="priority" xsi:type="string">4</argument>
            </arguments>
        </referenceBlock>
        <!-- MY OWN CUSTOM BLOCK ON THE SECOND POSITION -->
        <block class="Magento\Catalog\Block\Product\View\Description" name="product.features" as="features" template="product/view/features.phtml" group="detailed_info">
            <arguments>
                <argument translate="true" name="title" xsi:type="string">Features</argument>
                <argument name="priority" xsi:type="string">2</argument>
            </arguments>
        </block>
        <!-- MY OWN CUSTOM BLOCK ENDS HERE -->
    </referenceBlock>

Further more we have to adjust the details.phtml, so copy it from

<magento_root>/vendor/magento-catalog-view/frontend/templates/product/view/details.phtml

to

<magento_root>/app/design/frontend/<Vendor>/<theme>/Magento_Catalog/templates/product/view/details.phtml

Please keep in mind that magento's own details.phtml could be changed in future Magento versions or patches. These changes should be also applied to your theme's details.phtml

We now need to get the priority which we passed on via the XML file.

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

?>
<?php if ($detailedInfoGroup = $block->getGroupChildNames('detailed_info', 'getChildHtml')):?>
    <div class="product info detailed">
        <?php $layout = $block->getLayout(); ?>
        <?php
            # We create a new array;
            $newPriority = array();
            # forEach the original $detailedInfoGroup Array;
            foreach ($detailedInfoGroup as $name){
                $alias = $layout->getElementAlias($name);
                # Get the priority which we applied via xml file
                # If no priority is applied via xml file then just set it to 10
                $priority = $block->getChildData($alias,'priority') ? $block->getChildData($alias,'priority') : '10';
                # variables pushed into new two-dimensional array
                array_push($newPriority, array($name, $priority));
            }
            # Sort array by priority
            usort($newPriority, function($a, $b) {
                return $a['1'] <=> $b['1'];
            });
        ?>
        <div class="product data items" data-mage-init='{"tabs":{"openedState":"active"}}'>
            <?php
            # Delete the original forEach statement
            #foreach ($detailedInfoGroup as $name)
            foreach ($newPriority as $name):?>
                <?php
                    # rename $name[0] to $name because it's a two-dimensional array
                    # No further changes to this file, it works as explained
                    $name = $name[0];
                    $html = $layout->renderElement($name);
                    if (!trim($html)) {
                        continue;
                    }
                    $alias = $layout->getElementAlias($name);
                    $label = $block->getChildData($alias, 'title');
                ?>
                <div class="data item title"
                     aria-labeledby="tab-label-<?php /* @escapeNotVerified */ echo $alias;?>-title"
                     data-role="collapsible" id="tab-label-<?php /* @escapeNotVerified */ echo $alias;?>">
                    <a class="data switch"
                       tabindex="-1"
                       data-toggle="switch"
                       href="#<?php /* @escapeNotVerified */ echo $alias; ?>"
                       id="tab-label-<?php /* @escapeNotVerified */ echo $alias;?>-title">
                        <?php /* @escapeNotVerified */ echo $label; ?>
                    </a>
                </div>
                <div class="data item content" id="<?php /* @escapeNotVerified */ echo $alias; ?>" data-role="content">
                    <?php /* @escapeNotVerified */ echo $html; ?>
                </div>
            <?php endforeach;?>
        </div>
    </div>
<?php endif; ?>

So you see: You just have to add a few lines and can always change the priority/order of the tabs via the xml file, you don't have to change the details.phtml in future anymore.

  • How can we display content of "Detail" tab at the bottom of "More info" tab's bottom please ? – Jai Feb 3 '17 at 10:02
  • your question is not referring to the original question. you should open a new ticket. Anyway: You could reference to a new phtml file in your theme's catalog_product_view.xml (e.g. description-attributes-combined.phtml) and paste the content from the original descrption.phtml and attributes.phtml. – juhanix Feb 6 '17 at 13:56
  • I pasted content of both file in one and called in xml files as: <referenceBlock name="product.info.details"> <block class="Magento\Catalog\Block\Product\View\Description" name="product.info.description.attributes" template="product/view/description-attributes-combined.phtml" group="detailed_info"> <arguments> <argument translate="true" name="title" xsi:type="string">More Information</argument> </arguments> </block> </referenceBlock> But blank site and display only content of tabs. Whats missing? – Jai Feb 7 '17 at 9:35
  • I added question here: magento.stackexchange.com/q/157376/29175 – Jai Feb 9 '17 at 4:24
  • @juhanix This requires you to pull forward a template that could be modified in later Magento patches. See my answer which doesn't modify any core files. Hopefully, this is a core issue that can get patched. – LordZardeck Jul 14 '17 at 18:53
11

For Change position of Tabbing in the details page, Using XML configuration file after or before attribute not help in this case.

You have to change from template file.

Copy details.phtml file from core to your theme,

app/design/frontend/Packagename/themename/Magento_Catalog/templates/product/view/details.phtml

Inside this file, you can fetch all tabs name using, print_r($detailedInfoGroup) you have to get value like,

Array
(
    [0] => product.info.description
    [1] => product.attributes
    [2] => reviews.tab
)

You have to set according to your requirement in new array before foreach in file,

<?php $newOrderTabbing = array('product.info.description',,'reviews.tab','product.attributes'); //custom add ?>,

add <?php foreach ($newOrderTab as $name):?>,

Complete code in details.phtml is like below,

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

?>
<?php if ($detailedInfoGroup = $block->getGroupChildNames('detailed_info', 'getChildHtml')):?>
    <?php $newOrderTabbing = array('product.info.description','reviews.tab','product.attributes'); //custom added position ?>
    <div class="product info detailed">
        <?php $layout = $block->getLayout(); ?>
        <div class="product data items" data-mage-init='{"tabs":{"openedState":"active"}}'>
            <?php foreach ($newOrderTabbing as $name): //custom arrayname?>
                <?php
                    $html = $layout->renderElement($name);
                    if (!trim($html)) {
                        continue;
                    }
                    $alias = $layout->getElementAlias($name);
                    $label = $block->getChildData($alias, 'title');
                ?>
                <div class="data item title"
                     aria-labeledby="tab-label-<?php /* @escapeNotVerified */ echo $alias;?>-title"
                     data-role="collapsible" id="tab-label-<?php /* @escapeNotVerified */ echo $alias;?>">
                    <a class="data switch"
                       tabindex="-1"
                       data-toggle="switch"
                       href="#<?php /* @escapeNotVerified */ echo $alias; ?>"
                       id="tab-label-<?php /* @escapeNotVerified */ echo $alias;?>-title">
                        <?php /* @escapeNotVerified */ echo $label; ?>
                    </a>
                </div>
                <div class="data item content" id="<?php /* @escapeNotVerified */ echo $alias; ?>" data-role="content">
                    <?php /* @escapeNotVerified */ echo $html; ?>
                </div>
            <?php endforeach;?>
        </div>
    </div>
<?php endif; ?>
  • Thanks Rakesh Jesadiya, It's working like a charm...! – Sarfaraj Sipai Feb 14 at 11:15
2

I know that there have been other answers to this question, but all of them were too invasive for my taste. Looking into the issue, Magento adds a separate "group" attribute to an element and adds children to that attribute in the order loaded in the layout, completely separate from the children array that contains the sorted array of elements. In order to fix this, I wrote a simple around plugin that fixes the sorting when retrieving group children:

class Structure
{
    /**
     * Re-orders the array of group children based on the sort order defined on the parent's children
     *
     * @param \Magento\Framework\Data\Structure $subject
     * @param callable $proceed
     * @param $parentId
     * @param $groupName
     * @return array
     */
    function aroundGetGroupChildNames( \Magento\Framework\Data\Structure $subject, callable $proceed, $parentId, $groupName )
    {
        $sortedList = [];

        // Go ahead and get all the children
        $groupChildNames = $proceed( $parentId, $groupName );

        // If there was no group children, just leave early
        if (empty( $groupChildNames ))
        {
            return $groupChildNames;
        }

        // Go through the order of the parent's children and if it's in the list of group children aggregated above,
        // add it to our own list
        foreach ($subject->getElement( $parentId )['children'] as $childId => $childAlias)
        {
            if (!in_array( $childId, $groupChildNames ))
            {
                continue;
            }

            array_push( $sortedList, $childId );
        }

        return $sortedList;
    }
}

This will now allow you to order the tabs using the standard before and after attributes in layout XML as you would expect, and will probably not need to be modified on future Magento patches.

  • The mighty and honorable LordZardeck has given the correct answer. Adding so much logic to a template, as most of the above answers do, is just asking for trouble when updating to newer magento versions. – Nathan Toombs Jul 30 '18 at 20:30
2

I think you need just to add them in the order of your choice. For me, I'm using 4 tabs in this order:

  1. Details
  2. Product Tags
  3. Custom Tab 1
  4. Custom Tab 2

In my custom module I created this layout file: catalog_product_view.xml with this content:

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="product.info.details">
            <block class="Magento\Catalog\Block\Product\View" name="tab.tags" template="Godogi_Utilities::catalog/product/tab_tags.phtml" group="detailed_info" >
                <arguments>
                    <argument translate="true" name="title" xsi:type="string">Product Tags</argument>
                </arguments>
            </block>
            <block class="Magento\Catalog\Block\Product\View" name="tab.custom.tab.one" template="Godogi_Utilities::catalog/product/custom_tab_1.phtml" group="detailed_info" >
                <arguments>
                    <argument translate="true" name="title" xsi:type="string">Custom Tab 1</argument>
                </arguments>
            </block>
            <block class="Magento\Catalog\Block\Product\View" name="tab.custom.tab.n" template="Godogi_Utilities::catalog/product/custom_tab_n.phtml" group="detailed_info" >
                <arguments>
                    <argument translate="true" name="title" xsi:type="string">Custom Tab N</argument>
                </arguments>
            </block>
        </referenceBlock>
    </body>
</page>

You can see that I have added just 3 tabs because the details tab already exist. As a result I have got the tabs in this order:

  1. Product Tags
  2. Custom Tab 1
  3. Custom Tab 2
  4. Details

Which is not what I wanted, now my solution to this is to add also the Details tab again, so my layout file would be like this:

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="product.info.details">
            <block class="Magento\Catalog\Block\Product\View\Description" name="product.info.description" template="product/view/attribute.phtml" group="detailed_info">
                <arguments>
                    <argument name="at_call" xsi:type="string">getDescription</argument>
                    <argument name="at_code" xsi:type="string">description</argument>
                    <argument name="css_class" xsi:type="string">description</argument>
                    <argument name="at_label" xsi:type="string">none</argument>
                    <argument name="title" translate="true" xsi:type="string">Details</argument>
                </arguments>
            </block>
            <block class="Magento\Catalog\Block\Product\View" name="tab.tags" template="Godogi_Utilities::catalog/product/tab_tags.phtml" group="detailed_info" >
                <arguments>
                    <argument translate="true" name="title" xsi:type="string">Product Tags</argument>
                </arguments>
            </block>
            <block class="Magento\Catalog\Block\Product\View" name="tab.custom.tab.one" template="Godogi_Utilities::catalog/product/custom_tab_1.phtml" group="detailed_info" >
                <arguments>
                    <argument translate="true" name="title" xsi:type="string">Custom Tab 1</argument>
                </arguments>
            </block>
            <block class="Magento\Catalog\Block\Product\View" name="tab.custom.tab.n" template="Godogi_Utilities::catalog/product/custom_tab_n.phtml" group="detailed_info" >
                <arguments>
                    <argument translate="true" name="title" xsi:type="string">Custom Tab N</argument>
                </arguments>
            </block>
        </referenceBlock>
    </body>
</page>

Now I have the order that I want :) enter image description here

2

In Magento 2.3.1 or Above we can use sort_order argument in "app/design/frontend/Packagename/themename/Magento_Catalog/layout/catalog_product_view.xml" xml config

<referenceBlock name="product.info.description">
    <arguments>
        <argument name="title" translate="true" xsi:type="string">Description</argument>
        <argument name="sort_order" xsi:type="string">20</argument>
    </arguments>
</referenceBlock>

Also make sure to update method "getGroupChildNames" with "getGroupSortedChildNames" in template file (If you are overriding it) "app/design/frontend/Packagename/themename/Magento_Catalog/templates/product/view/details.phtml".

Old Method

<?php if ($detailedInfoGroup = $block->getGroupChildNames('detailed_info', 'getChildHtml')):?>

Updated Method

<?php if ($detailedInfoGroup = $block->getGroupSortedChildNames('detailed_info', 'getChildHtml')):?>

If you are overriding Magento\Catalog\Block\Product\View\Details.php block class then your block class should have that updated method "getGroupSortedChildNames" as well.

1

The easiest and best way is in my opinion the solution of LordZardeck with a plugin. After updating the vendor/module/etc/frontend/di.xml

    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    ...

        <type name="Magento\Framework\Data\Structure">
            <plugin name="vendor_sort_tabs" type="Vendor\Module\Plugins\Structure" sortOrder="0"/>
        </type>

   ...

    </config>

everything worked as desired.

Thanks @LordZardeck for the clean code!

0

Here is the solution I used. It will swap the description and attributes tab if they are both available. This is using the Ultimo theme. But you will get the point. KISS.

<?php 
$detailedInfoGroup = $block->getGroupChildNames('detailed_info', 'getChildHtml');

if ($detailedInfoGroup[0] == 'product.info.description' && $detailedInfoGroup[1] == 'product.attributes') {
    $detailedInfoGroup[0] = 'product.attributes';
    $detailedInfoGroup[1] = 'product.info.description';
}

// rest of the code to look through $detailedInfoGroup
?>
0

My solution to the issue is to modify the template details.phtml so it gets the children blocks from the layout.

$blocks = $layout->getChildBlocks($block->getNameInLayout());

By doing it this way, it respects the order given by after and before modificators.

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

?>
<?php if ($detailedInfoGroup = $block->getGroupChildNames('detailed_info', 'getChildHtml')):?>
    <div class="product info detailed">
        <?php $layout = $block->getLayout(); ?>
        <?php $blocks = $layout->getChildBlocks($block->getNameInLayout());?>
        <div class="product data items" data-mage-init='{"tabs":{"openedState":"active"}}'>
            <?php foreach($blocks as $alias=>$child_block):?>
                <?php if(in_array($child_block->getNameInLayout(),$detailedInfoGroup)):?>
                    <?php
                        $html = $child_block->toHtml();
                        if (!trim($html)) {
                            continue;
                        }       
                        $label = $child_block->getData('title');
                    ?>
                    <div class="data item title"
                         aria-labeledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title"
                         data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>">
                        <a class="data switch"
                           tabindex="-1"
                           data-toggle="switch"
                           href="#<?= /* @escapeNotVerified */ $alias ?>"
                           id="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title">
                            <?= /* @escapeNotVerified */ $label ?>
                        </a>
                    </div>
                    <div class="data item content" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content">
                        <?= /* @escapeNotVerified */ $html ?>
                    </div>
                <?php endif; ?>
            <?php endforeach;?>
        </div>
    </div>
<?php endif; ?>

I still use the array provided by getGroupChildNames in order to validate if the block belongs to the group.

if(in_array($child_block->getNameInLayout(),$detailedInfoGroup))

0

I didn't want to work on the theme, I wanted to modify 'getGroupChildNames' method behavior. This approach should work even if the template gets modified.

I added this to catalog_product_view.xml:

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column"
  xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
  <body>
    <!-- CODE TO REORDER PRODUCT TABS -->
    <block class="Dsy\Ton\Block\Product\View\Description" name="product.info.details.new" template="Magento_Catalog::product/view/details.phtml">
        <!-- ADD MORE BOCKS IF NEEDED -->
    </block>
    <move element="product.info.details.new" destination="content" after="product.info.details"/> 

    <move element="product.info.description" destination="product.info.details.new" after="-"/>
    <move element="product.attributes" destination="product.info.details.new" after="-"/> 

    <referenceBlock name="product.info.details" remove="true"/>
    <!-- CODE TO REORDER PRODUCT TABS -->
  </body>

Then, create a block to change the 'getGroupChildNames' behavior:

<?php

namespace My\Module\Block\Product\View;

use Magento\Catalog\Model\Product;

class Description extends \Magento\Catalog\Block\Product\View\Description
{
    public function getGroupChildNames($groupName)
    {
        if ('detailed_info' === $groupName) {
            return [
                // here you can change the order
                'product.attributes',
                'product.info.description',
            ];
        }

        return parent::getGroupChildNames($groupName);
    }
}

That's all.

0

LordZardeck has the best answer, but this is basically a bug and should be fixed in the core.

The simplest solution to the problem i found is as following: Override the Magento_Catalog::product/view/details.phtml template and after the first php condition on line 10:

if ($detailedInfoGroup = $block->getGroupChildNames(...

add following code to modify the order:

$_prepend = array_reverse(['product.overview.description']);
foreach ($_prepend as $_name) {
    $k = array_search($_name,$detailedInfoGroup);
    if ( $k !== false) {
        unset($detailedInfoGroup[$k]);
        array_unshift($detailedInfoGroup,$_name);
    }
}

this will change the order and push all tabs listed in $_prepend to the beginning of the array in the order defined.

0

Looks like all of the above is not relevant anymore. As of Magento 2.3.1 we can use sort_order argument in xml config.

0

A plugin is a nice solution. But you can still improve on it. You don't want to write a different plugin every time you need to reorder the tabs. What you want to do is set the order in xml. Like this:

<referenceBlock name="product.info.details">
        <referenceBlock name="product.info.description">
            <arguments>
                <argument name="priority" xsi:type="number">1</argument>
            </arguments>
        </referenceBlock>
        <referenceBlock name="product.attributes">
            <arguments>
                <argument name="priority" xsi:type="number">3</argument>
            </arguments>
        </referenceBlock>
        <referenceBlock name="reviews.tab">
            <arguments>
                <argument name="priority" xsi:type="number">4</argument>
            </arguments>
        </referenceBlock>
        <!-- MY OWN CUSTOM BLOCK ON THE SECOND POSITION -->
        <block class="Magento\Catalog\Block\Product\View\Description" name="product.features" as="features" template="product/view/features.phtml" group="detailed_info">
            <arguments>
                <argument translate="true" name="title" xsi:type="string">Features</argument>
                <argument name="priority" xsi:type="number">2</argument>
            </arguments>
        </block>
        <!-- MY OWN CUSTOM BLOCK ENDS HERE -->
    </referenceBlock>

and then, instead of modifying the template, create a plugin that would make Magento understand the priority argument used in xml config:

class TabOrder
{
    const TABS_BLOCK_NAME = 'product.info.details';
    const ORDER_ARGUMENT_NAME = 'priority';

    public function afterGetGroupChildNames(
        \Magento\Catalog\Block\Product\View\Description $subject,
        array $result
    ) {
        if ($subject->getNameInLayout() === self::TABS_BLOCK_NAME) {
            foreach ($result as $blockName) {
                // get priority for each block and include it in modifiedResult
                $alias = $subject->getLayout()->getElementAlias($blockName);

                // 100 default value guarantees the tab without priority argument goes last
                $blockPosition =
                    $subject->getChildData($alias, self::ORDER_ARGUMENT_NAME) ?? 100;
                $modifiedResult[] = array(
                    $blockName,
                    $blockPosition);
            }

            // order elements from $modifiedResult by priority
            usort($modifiedResult, function ($a, $b) {
                return $a[1] <=> $b[1];
            });

            // remove priority and leave original values only
            array_walk($modifiedResult, function (&$value, $key) {
                $value = $value[0];
            });

            return $modifiedResult;
        }

        return $result;
    }
}

And finally the plugin must be applied to the Magento\Catalog\Block\Product\View\Description class in di.xml file.

0

For Magento 2 change order of tabs on product page.

You can easily customise order of tab in simple way.

  1. Create details.phtml file in

app/design/frontend/vendor/theme/Magento_Catalog/templates/product/view/

If details.phtml file already exist so update it.

  1. Add this code before foreach loop. Define "$detailedInfoGroup" array.

Original code:

<?php if ($detailedInfoGroup = $block->getGroupChildNames('detailed_info', 'getChildHtml')):?>
    <div class="product info detailed">
        <?php $layout = $block->getLayout(); ?>
        <div class="product data items" data-mage-init='{"tabs":{"openedState":"active"}}'>
            <?php foreach ($detailedInfoGroup as $name):?>
                <?php
                    $html = $layout->renderElement($name);
                    if (!trim($html)) {
                        continue;
                    }
                    $alias = $layout->getElementAlias($name);
                    $label = $block->getChildData($alias, 'title');
                ?>
                <div class="data item title"
                     aria-labeledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title"
                     data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>">
                    <a class="data switch"
                       tabindex="-1"
                       data-toggle="switch"
                       href="#<?= /* @escapeNotVerified */ $alias ?>"
                       id="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title">
                        <?= /* @escapeNotVerified */ $label ?>
                    </a>
                </div>
                <div class="data item content" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content">
                    <?= /* @escapeNotVerified */ $html ?>
                </div>
            <?php endforeach;?>
        </div>
    </div>
<?php endif; ?> 

After add code:

<?php if ($detailedInfoGroup = $block->getGroupChildNames('detailed_info', 'getChildHtml')):?>
    <div class="product info detailed">
        <?php $layout = $block->getLayout(); ?>
        <div class="product data items" data-mage-init='{"tabs":{"openedState":"active"}}'>
            <?php $detailedInfoGroup = ["product.info.description", "product.attributes", "reviews.tab"]; ?>
            <?php foreach ($detailedInfoGroup as $name):?>
                <?php
                    $html = $layout->renderElement($name);
                    if (!trim($html)) {
                        continue;
                    }
                    $alias = $layout->getElementAlias($name);
                    $label = $block->getChildData($alias, 'title');
                ?>
                <div class="data item title"
                     aria-labeledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title"
                     data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>">
                    <a class="data switch"
                       tabindex="-1"
                       data-toggle="switch"
                       href="#<?= /* @escapeNotVerified */ $alias ?>"
                       id="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title">
                        <?= /* @escapeNotVerified */ $label ?>
                    </a>
                </div>
                <div class="data item content" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content">
                    <?= /* @escapeNotVerified */ $html ?>
                </div>
            <?php endforeach;?>
        </div>
    </div>
<?php endif; ?>

And add custom tab in product page check this link

Magento 2 - Create product tab that displays custom attribute

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.