I am trying to build a menu with typoscript, in which the 2nd level differs depending on the 1st level menu items - this is what I've tried so far, but it doesn't work as wanted:
lib.mainnav = HMENU
lib.mainnav {
wrap = <ul id="nv-main">|<li class="responav"><i class="fas fa-bars"></i></li></ul>
1 = TMENU
1 {
expAll = 1
NO = 1
NO.wrapItemAndSub = <li>|</li>
NO.stdWrap.htmlSpecialChars = 1
CUR = 1
CUR.wrapItemAndSub = <li class="current">|</li>
CUR.stdWrap.htmlSpecialChars = 1
}
# von 1 (TMENU) kopieren
2 < .1
2 {
expAll = 1
wrap = <div class="nv-main-sub-bg"><div class="nv-main-sub-wrapper clearfix">|</div></div>
wrap.override = <div class="nv-main-sub-bg"><div class="nv-main-sub-wrapper indented clearfix">|</div></div>
wrap.override.if {
value = 6
equals.data = page:uid
}
stdWrap.cObject = COA
stdWrap.cObject {
20 = HMENU
20 {
special = directory
special.value.data = field:pid
1 = TMENU
1 {
wrap = <div class="nv-main-col-sub"><ul>|</ul></div>
NO = 1
NO.allWrap = <li>|</li>
NO.stdWrap.htmlSpecialChars = 1
NO.after.cObject = COA
NO.after.cObject {
wrap = <div class="nv-main-info-wrapper"><div class="nv-main-col-info">|</div></div>
# Text aus dem Feld subtitle oder title der Seiteneigenschaften auslesen
10 = TEXT
10 {
value.field = subtitle // title
stdWrap.wrap = <p>|</p>
}
# Bild aus Reiter Resourcen der Seiteneigenschaften auslesen (nur bei Page-UID 4)
20 = FILES
20 {
if.value.field = pid
if.equals = 4
references {
table = pages
# Seiten-ID
uid.dataWrap= {field:uid}
fieldName = media
}
renderObj = IMAGE
renderObj {
file.width = 290c
file.height = 200c
file.import.data = file:current:uid
file.crop.data = file:current:crop
file.treatIdAsReference = 1
altText.data = file:current:title
##params = class="img-responsive"
wrap = |
}
}
# Text aus dem Feld "abstract" auslesen (nur bei Page-UID 6)
30 = TEXT
30 {
if.equals.field = pid
if.value = 6
value.field = abstract
stdWrap.wrap = <p class="b-sub">|</p>
}
}
CUR < .NO
CUR.allWrap = <li class="current">|</li>
}
}
}
}
}
This will create a 2-level-menu, where the 2nd level is a combination of the menu items each with a text info and a picture which already works. What I want is a different 2nd-level menu depending on the selected 1st level item. For this I need to change the wrapping and replace the image by additional text.
Can you please give me a hint? Thank you very much for your help!
Michael
EDIT:
Typo3 V. 8.7.16
Page Tree:
Item 1
-- Subitem 1 **with text and picture**
-- Subitem 2 **with text and picture**
-- Subitem 3 **with text and picture**
-- ...
-- Subitem XX **with text and picture**
Item 2
-- Subitem 1 **with text and picture**
-- Subitem 2 **with text and picture**
-- Subitem 3 **with text and picture**
-- ...
-- Subitem XX **with text and picture**
Item 3
-- Subitem 1 **only with text** (headline and copytext)
-- Subitem 2 **only with text** (headline and copytext)
-- Subitem 3 **only with text** (headline and copytext)
-- ...
-- Subitem XX **only with text** (headline and copytext)
Item 4
-- Subitem 1 ...
-- ...
Item 5
-- Subitem 1 ...
-- ...
HTML-structure:
<ul id="nv-main">
<li>
Item 1
<div class="nv-main-sub-bg">
<div class="nv-main-sub-wrapper clearfix">
<div class="nv-main-col-sub">
<ul>
<li>
Subitem 1 ** with text and picture **
<div class="nv-main-info-wrapper">
<div class="nv-main-col-info">
<p>XXX</p> <!— from title/subtitle —>
<img src=„XXX“ border="0"> <!— from Resources —>
</div>
</div>
</li>
<li> … </li>
<li> … </li>
<li> … </li>
…
</ul>
</div>
</div>
</div>
</li>
<li> … </li>
<li>
Item 3
<div class="nv-main-sub-bg">
<div class="nv-main-sub-wrapper **indented** clearfix">
<div class="nv-main-col-sub">
<ul>
<li>
Subitem 1 ** only with text **
<div class="nv-main-info-wrapper">
<div class="nv-main-col-info">
<p class="b-head">XXX</p> <!— from title/subtitle —>
<p class="b-sub">XXX</p> <!— from abstract —>
</div>
</div>
</li>
<li> … </li>
<li> … </li>
<li> … </li>
…
</ul>
</div>
</div>
</div>
</li>
<li> … </li>
<li> … </li>
</ul>
One solution would be to render all information for the second level and decide by CSS which parts will be shown (depending on a matching class set on the first level by a property of the level one page)
The other way would be (as Joey mentioned): build your .after object depending on the pid of the current page. either you have a list of pages, which have the special rendering 'only with text' or (more complicated) you use the pid to access a property of the level one page which indicates this special rendering.
The easy solution with a list of page uids could be:
2.after.cObject = CASE
2.after.cObject {
key.field = pid
default = COA
default {
// rendering of **with text and picture**
}
// uid of level1 page
123 = COA
123 {
// rendering of **only with text**
}
// alternative level1 page with this rendering:
234 < .123
}
If your option **only with text** means the same as **with text and picture** just without the picture you can modify your existing code (which seems very complictaed using the HMENU with special=directory) to blank the the picture:
NO.after.cObject.20.if {
isInList.field = pid
// store pages uids in constant like: 1,2,34,50,87
value = {$specialPagesUidsList}
negate = 1
}
I recently had a similar problem.
Second and third level navigation had to be the depending to the chosen item of the first level.
Do you need to have the second leven nested into the active item?
<ul class="first-level">
<li>item 1</li>
<li>item 2
<ul class="second-level">
<li>item 3</li>
</li>
</ul>
or could you handle a structure like this?
<ul class="first-level">
<li>item 1</li>
<li>item 2</li>
</ul>
<ul class="second-level">
<li>item 3</li>
</ul>
Usually dealing with the "position" within a menu is a job for option split, but this would need a completely rewritten second level, since it had to be put to the "after" part of the first level items.
But since the behaviour of a submenu is actually determined by the parent page it belongs to and not just by the position this parent page has got within the first level of the menu, you should go for a specific PID value instead of the position.
You can use "if" or a "cObject" CASE for that purpose, so that the second level can be changed based on the pid value of its items.
Related
i am wondering if there is a way in TYPOSCRIPT to count the childrens in a menu.
The output should be something like:
<ul class="ebene1" data-elements="4">
<li>Element 1</li>
<li>Element 2</li>
<li>Element 2</li>
<li>Element 2</li>
</ul>
My Code looks like this:
script.NAV = HMENU
script.NAV {
special = directory
special.value = 6
1 = TMENU
1 {
expAll = 1
noBlur = 1
wrap = <ul class="menu main" id="nav-main">|</ul>
NO = 1
NO.wrapItemAndSub = <li class="first element1">|</li> |*| <li class="element1">|</li> |*| <li class="last element1">|</li>
CUR = 1
CUR.wrapItemAndSub = <li class="first element1 current">|</li> |*| <li class="element1 current">|</li> |*| <li class="last element1 current">|</li>
...
Thanks, any help is really appreciated.
Use the register {register:count_MENUOBJ} like this should solve your problem.
1 = TMENU
1 {
NO = 1
NO {
wrapItemAndSub.insertData = 1
wrapItemAndSub = <li class="element-{register:count_MENUOBJ}">|</li>
}
}
Actually although that may be done as #Marcus showed (I believe that snippet works, didn't check) it is probably faster approach to us JavaScript for this; as easy as:
<ul class="ebene1">
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
<li>Element X</li>
</ul>
<script>$('.ebene1').attr('data-elements', $('.menu > li').length);</script>
And CSS like:
.ebene1[data-elements="4"] {
background-color: yellow;
}
On the other hand...
If you have dynamic menu with unknown number of items, instead adding many CSS declarations like,
.ebene1[data-elements="3"] li {width: 33.333%;}
.ebene1[data-elements="4"] li {width: 25%;}
.ebene1[data-elements="5"] li {width: 20%;}
Avoid adding the data- attribute via JS and just change the width by calculating the correct value:
$('.ebene1 > li').css({width: 100 / $('.ebene1 > li').length + '%'});
You need the stdWrap function numrows. There you can add any table together with a select statement. Here is an example:
lib.countmenu = COA
lib.countmenu {
10 = HMENU
10.1 = TMENU
10.1 {
wrap = <ul>|</ul>
NO {
stdWrap.cObject = COA
stdWrap.cObject {
30 = TEXT
30.numRows.table = pages
30.numRows.select.pidInList.field = uid
30.numRows.select.where = nav_hide!=1 AND doktype!=5 AND doktype!=6
30.dataWrap = {field:title} [|]
}
wrapItemAndSub = <li>|</li>
}
}
}
in my TYPO3 7.5 project I have created some custom page types by registering them in ext_tables.php as described further here
I can select those page types in the backend, no errors, all good. Also checking for a certain page-type via statements in the frontend works fine.
Now I want to create a menu of those (sub-)page trees. The TypoScript solution looks like this and works fine:
lib.tourTeasers = HMENU
lib.tourTeasers {
special = directory
wrap = <section class="row">|</section>
1 = TMENU
1 {
wrap = <div class="col-sm-12">|</div>
expAll = 1
NO {
doNotLinkIt = 1
linkWrap = <h2 style="text-align:right">Kategorie: |</h2>
}
}
2 = TMENU
2{
expAll = 1
NO{
doNotLinkIt = 1
linkWrap = <h3>Tourtyp: |</h3>
}
}
3 = TMENU
3{
NO{
doNotLinkIt = 1
linkWrap = <h4>|</h4>
after.cObject = COA
after.cObject {
stdWrap.dataWrap = <div class="row teaser">|</div>
10 = FILES
10 {
references {
table = pages
fieldName = media
}
renderObj=IMAGE
renderObj{
file{
width=300c
height=150c
#maxW=257c
#maxH=150c
import.data= file:current:publicUrl
}
altText.data = file:current:title
#altText.field=abstract
#titleText.field=nav_title
#stdWrap.dataWrap = |</a>
stdWrap.dataWrap(
<a href="index.php?id={field:uid}" title="Tour {field:title} ansehen">
<div class="col-sm-4 teaser__image">|</div>
</a>
)
params = class="img-responsive"
}
}
30 = TEXT
30.field = abstract // bodytext
30.crop = 250
30.wrap = <div class="col-sm-8 teaser__description"><p>|</p>
40 = TEXT
40.value = Ansehen
40.typolink.parameter.field = uid
40.typolink.ATagParams = class="btn"
40.wrap = <div class="button teaser__cta">|</div></div>
}
}
}
}
My problem is:
If I use the very Fluid Viewhelper that is intended to render exacly the same
<v:page.menu.directory pages="{page_uid}" as="tours" expandAll="true">
<f:for each="{tours}" as="tour">
<div class="row">
<div class="col-sm-3">
<v:page.resources.fal table="pages" field="media" uid="{tour.uid}" as="images">
<f:for each="{images}" as="image">
<f:image src="{image.url}" alt="{image.alternative} {image.name}" title="{image.title}" class="img-responsive"/>
</f:for>
</v:page.resources.fal>
</div>
<div class="col-sm-9">
<h3>{tour.title}</h3>
{tour.abstract}
</div>
</div>
</f:for>
</v:page.menu.directory>
{page_uid} being the parent page, nothing is displayed.
If I change some of the sub-pages' doktype back to "default" they are displayed in the rendered menu.
any idea as to why this is? Am i missing a certain argument in the viewhelper? i also tried to set the allowed doktypes in the viewhelper but that doesn't change anything.
thanks for the reponses, indeed it had to do with the doktype, is I didn't include the "new" ones for both the parent pages AND their sub-pages. Works now, solved. Fluid really rocks
By default the second level HMENU is rendered after the first element.
foo = HMENU
foo {
1 = TMENU
1.noBlur = 1
1.NO = 1
1.NO.expAll = 1
1.NO.wrap = <li class="second">|</li>
2 = TMENU
2.noBlur = 1
2.NO = 1
2.NO.wrap = <li class="second">|</li>
}
Default HTML :
<li>firstlevel 1</li>
<li class="second">secondlevel 1</li>
<li>firstlevel 2</li>
But what I want is :
<li>
firstlevel 1
<li class="second">secondlevel 1</li>
</li>
<li>firstlevel 2</li>
i.e. The second level is rendered inside the first, not after it. Any ideas appreciated!
You need to use:
1.NO.wrapItemAndSub
instead of:
1.NO.wrap
But you lack <ul> in your menu. It will not validate.
The proper menu should look smth like that:
foo = HMENU
foo {
1 = TMENU
1.wrap = <ul class="first">|</ul>
1.noBlur = 1
1.expAll = 1
1.NO.wrapItemAndSub = <li>|</li>
2 < .1
2.wrap = <ul class="second">|</ul>
}
Edit: Moved expAll from "1.NO" to "1"
I have the below typoscript
lib.nav = HMENU
lib.nav {
wrap = <ul> | </ul>
1 = TMENU
1 {
expAll=1
NO {
|*| <ul><li> | </li></ul> |*| <ul><li> | </li></ul> || <ul><li class="last"> | </li></ul> |*|
wrapItemAndSub = |*| <li> | </li> |*| <li> | </li> || <li class="last"> | </li> |*|
stdWrap.cObject = CASE
stdWrap.cObject {
key.field = doktype
default = TEXT
default.field = title
default.typolink.parameter.data = field:uid
default.typolink.ATagBeforeWrap=0
3 < .default
3 {
stdWrap.htmlSpecialChars = 1
typolink {
parameter {
data >
dataWrap = http://{field:url}
}
}
}
}
doNotLinkIt = 1 # to avoid "<a>" repeated twice
subst_elementUid = 1
}
}
2 = TMENU
2 {
expAll=1
wrap = <ul> | </ul>
NO {
wrapItemAndSub = |*| <li> | </li> |*| <li> | </li> || <li> | </li> |*|
stdWrap.cObject = CASE
stdWrap.cObject {
key.field = doktype
default = TEXT
default.field = title
default.typolink.parameter.data = field:uid
default.typolink.ATagBeforeWrap=0
3 < .default
3 {
stdWrap.htmlSpecialChars = 1
typolink {
parameter {
data >
dataWrap = http://{field:url}
}
}
}
}
doNotLinkIt = 1 # to avoid "<a>" repeated twice
subst_elementUid = 1
}
}
}
Which seems to work well, however I have notice a slight glitch when I have an external url page in the nav. It generates the below code for an external url.
<ul>
<li>External Links
<ul>
<li><a target="_top" href="http://example.com/1">Example 1</a></li>
<li><a target="_top" href="http://example.com/2">Example 2</a></li>
<li><a target="_top" href="http://example.com/3">Example 3</a></li>
</ul>
</li>
</ul>
How do I remove this default setting? I want it to be configurable per page in the behaviour tab (link target field in LTS 4.5).
I also have the below set in my constants.
PAGE_TARGET =
content.pageFrameObj =
Set in setup the extTarget via config. It is default "_top".
# just set it to an empty string instead of _top
config.extTarget =
I have a header menu and try to define different CSS classes for each item.
This is what I have:
20 = HMENU
20 {
special = directory
special.value = 107
1 = TMENU
1 {
wrap = <ul class="foo" id="mymenu">|</ul>
expAll = 1
NO = 1
NO.allWrap = <li class="first menu_{field:uid}">|</li> || <li class="menu_{field:uid}">|</li> || <li class="last menu_{field:uid}">|</li>
}
}
But in the HTML output I simply get class="first menu_{field:uid}" and nothing is replaced.
By the way: The page entries in the menu are links to external pages.
EDIT:
As our designer complained about the non-speaking class IDs we are abusing the body-class attribute now:
NO.allWrap = <li class="first {field:bodyclass_wrap_class}">|</li> || <li class="{field:bodyclass_wrap_class}">|</li> || <li class="last {field:bodyclass_wrap_class}">|</li>
You have to add NO.allWrap.insertData = 1, then the data will be inserted. allWrap is just a normal stdWrap, so the default features apply there.
BTW: I think your option split is still wrong. I guess you want to have it like this:
NO.allWrap = <li class="first menu_{field:uid}">|</li> |*| <li class="menu_{field:uid}">|</li> |*| <li class="last menu_{field:uid}">|</li>