# HorizontalMenu Selected Tab Highlighting

**URL:** <https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747>\
**Category:** Ignition\
**Tags:** perspective, scripting\
**Created:** [January 10, 2025, 7:47pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747 "2025-01-10T19:47:52Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![William\_Howard](https://avatars.discourse-cdn.com/v4/letter/w/a4c791/32.png) [@William\_Howard](https://forum.inductiveautomation.com/u/William_Howard)\
**Post date:** [January 10, 2025, 7:47pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/1 "2025-01-10T19:47:52Z")

</div>

I'm working with the Horizontal Menu component and im trying to get something similar to what is on the tab containers where when you select a tab it will highlight. I've gotten a script working that does this for are top level of tabs but if I have more then one layer of tabs the others don't get highlighted and Im looking for some help making it so that they will. I've made a for loop on a test script and was able to get one more layer of those tabs to highlight but Im hopping there is a way to make it dynamic so I don't need to add a for loop each time we add another layer to are menu. If anyone has already made a script for the menu that does what Im talking about.

---

<div class="post-metadata">

**Author:** ![William\_Howard](https://avatars.discourse-cdn.com/v4/letter/w/a4c791/32.png) [@William\_Howard](https://forum.inductiveautomation.com/u/William_Howard)\
**Post date:** [January 10, 2025, 7:50pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/2 "2025-01-10T19:50:19Z")

</div>

(Bellow is the script i made that was able to get the first two layers of the menu to be highlighted but things fell apart when i tried adding the 3rd layer)

```python
	# look through the entire menu tree
	for i in range(len(self.props.items)):
		# if the current index is the index we are looking for
		if i == currentValue.value[0].value:
			# change background color to yellow
			self.props.items[i].style.classes = "Components/HorizontalMenu/HorizontalMenu_Selected"
			# if we have a sub index tree selected
			if self.props.items[i].items is not None:
				# perform the same operation as above
				for j in range(len(self.props.items[i].items)):
					if j == currentValue.value[1].value:
						self.props.items[i].items[j].style.classes = "Components/HorizontalMenu/HorizontalMenu_Selected"
						if self.props.items[i].items[j].items is not None:
							# perform the same operation as above
							for t in range(len(self.props.items[i].items[j].items)):
								if t == currentValue.value[1].value:
									self.props.items[i].items[j].items[t].style.classes = "Components/HorizontalMenu/HorizontalMenu_Selected"
								else:
									self.props.items[i].items[j].items[t].style.classes = "Components/HorizontalMenu/HorizontalMenu_Deselected"
					# deselect the previous selected tabs
					else:
						self.props.items[i].items[j].style.classes = "Components/HorizontalMenu/HorizontalMenu_Deselected"
		else:
			# change the background color to black
			self.props.items[i].style.classes = "Components/HorizontalMenu/HorizontalMenu_Deselected"
			if self.props.items[i].items is not None:
				# perform the same operation as above
				for j in range(len(self.props.items[i].items)):
					self.props.items[i].items[j].style.classes = "Components/HorizontalMenu/HorizontalMenu_Deselected"
					if self.props.items[i].items[j].items is not None:
						# perform the same operation as above
						for t in range(len(self.props.items[i].items[j].items)):
							self.props.items[i].items[j].items[t].style.classes = "Components/HorizontalMenu/HorizontalMenu_Deselected"

```

---

<div class="post-metadata">

**Author:** ![William\_Howard](https://avatars.discourse-cdn.com/v4/letter/w/a4c791/32.png) [@William\_Howard](https://forum.inductiveautomation.com/u/William_Howard)\
**Post date:** [January 10, 2025, 7:53pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/3 "2025-01-10T19:53:52Z")

</div>

(Bellow is another attempt at the script above but is slightly different and doesn't have a for loop implemented yet.)

```python
	if len(currentValue.value) > 0:
	    # grab index out of the list for top-level item
	    itemIndex = currentValue.value[0].value
# # highlight the corresponding menu item's style
	    self.props.items[itemIndex]["style"]["classes"] = "Components/HorizontalMenu/HorizontalMenu_Selected"
	
	# now, do similar logic for unhighlighting
	if len(previousValue.value) > 0:
	    itemIndex = previousValue.value[0].value
	    self.props.items[itemIndex]["style"]["classes"] = "Components/HorizontalMenu/HorizontalMenu_Deselected"

```

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [January 10, 2025, 8:46pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/4 "2025-01-10T20:46:44Z")

</div>

Best bet is to make a recursive function. Entirely untested bash of code that I use for highlighting searched terms in a tree display (which has the same item format as the menu):

```python
SELECTED_CLASS = "Components/HorizontalMenu/HorizontalMenu_Selected"
UNSELECTED_CLASS = "Components/HorizontalMenu/HorizontalMenu_Deselected"

def recursiveSetHighlight(items, selection, idx=0):
	"""
	Recursively searches through a menu structure and highlights selected item and parent items
	"""
	highlightParent = False
	for itemIdx, item in enumerate(items):
		if idx < len(selection) and itemIdx == selection[idx]:
			item['style']['classes'] = SELECTED_CLASS
			highlightParent = True

		# Remove leading spaces left over from last search pass
		else:
			item['style']['classes'] = UNSELECTED_CLASS

		if len(item['items']):
			item['items'], childHasHighlight = recursiveSetHighlight(item['items'], selection, idx+1)
			# If any child has a highlight, mark that the parent of this item needs to be highlighted
			if childHasHighlight:
				item['style']['classes'] = SELECTED_CLASS
				highlightParent = True

	return items, highlightParent

```

`selection` is expecting a list of node indexes, like `[0,1,2,3,0].` Based on your code you could probably do `[item.value for item in currentValue.value]` to convert your selection data to the expected format.

The expected use of this function is

```python
selection = [item.value for item in currentValue.value]
cleanedStructure = project.util.sanitizeIgnitionObject(self.props.items)
highlightedStructure = project.menu.recursiveSetHighlight(cleanedStructure, selection)[0]
self.props.items = highlightedStructure

```

Util Function:

```python
def sanitizeIgnitionObject(element):
	if hasattr(element, ' __iter__'):
		if hasattr(element, 'keys'):
			return dict((k, sanitizeIgnitionObject(element[k])) for k in element.keys())
		else:
			return list(sanitizeIgnitionObject(x) for x in element)
	return element

```

---

<div class="post-metadata">

**Author:** ![William\_Howard](https://avatars.discourse-cdn.com/v4/letter/w/a4c791/32.png) [@William\_Howard](https://forum.inductiveautomation.com/u/William_Howard)\
**Post date:** [January 10, 2025, 8:58pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/5 "2025-01-10T20:58:09Z")

</div>

I will give this a try thanks for the response.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [January 10, 2025, 9:11pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/6 "2025-01-10T21:11:15Z")

</div>

Do yourself a quick favor before testing this code, copy your menu items to another custom property somewhere as a backup in case the function has issues.

If for some reason it mangles your menu items then you can just copy/paste from the backup instead of rebuilding your menu by hand

---

<div class="post-metadata">

**Author:** ![William\_Howard](https://avatars.discourse-cdn.com/v4/letter/w/a4c791/32.png) [@William\_Howard](https://forum.inductiveautomation.com/u/William_Howard)\
**Post date:** [January 10, 2025, 9:14pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/7 "2025-01-10T21:14:41Z")

</div>

Good idea thanks

---

<div class="post-metadata">

**Author:** ![William\_Howard](https://avatars.discourse-cdn.com/v4/letter/w/a4c791/32.png) [@William\_Howard](https://forum.inductiveautomation.com/u/William_Howard)\
**Post date:** [January 10, 2025, 9:58pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/8 "2025-01-10T21:58:16Z")

</div>

Ok I implemented the code you had given and it seem to work in selecting and deselecting but there is a problem if you have two tabs that can then go a 3rd layer deep they will both be highlighted along with what ever tab you select under on the respective numbered one under the other will also be highlighted.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/a/6/a60165aaf3b857d27434fe90ca7d3aec7c013d6b.png)  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/7/c721d0d71382d6edbd86b61400d68945689598db.png)  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/0/202ed98ab71b25bcb3eef67206541b464748ecce.png)

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [January 10, 2025, 10:37pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/9 "2025-01-10T22:37:28Z")

</div>

Ah, I messed up. My code is only looking that the current level item index matches the number in the selection list at the same level, without checking if the parent is selected.

Originally the code was for looking for text in each item in a tree, so it would highlight multiple things in every level. I just took that and modified it for my example code. Whoops.

Fixed code, also smaller:

```python
def recursiveSetHighlight(items, selection, parentSelected=False, idx=0):
	"""
	Recursively searches through a menu structure and highlights selected item and its parents
	"""
	selected = False
	for itemIdx, item in enumerate(items):
		# Allow item highlight if this is first level of menu or
		# item index matches number at same level in selection list
		if (parentSelected or idx == 0) and idx < len(selection) and itemIdx == selection[idx]:
			item['style']['classes'] = SELECTED_CLASS
			selected = True

		# If this is not the item we want, unselect it
		else:
			item['style']['classes'] = UNSELECTED_CLASS
			selected = False

		# Apply Highlighting to child items
		if len(item['items']):
			item['items'] = recursiveSetHighlight(item['items'], selection, selected, idx+1)

	return items

```

Remove the `[0]` after `recursiveSetHighlight` in your call to use the function:

```python
selection = [item.value for item in currentValue.value]
cleanedStructure = project.util.sanitizeIgnitionObject(self.props.items)
highlightedStructure = project.menu.recursiveSetHighlight(cleanedStructure, selection)
self.props.items = highlightedStructure

```

Edit: added missing `selected = False` in else statement

---

<div class="post-metadata">

**Author:** ![William\_Howard](https://avatars.discourse-cdn.com/v4/letter/w/a4c791/32.png) [@William\_Howard](https://forum.inductiveautomation.com/u/William_Howard)\
**Post date:** [January 13, 2025, 5:03pm UTC](https://forum.inductiveautomation.com/t/horizontalmenu-selected-tab-highlighting/100747/10 "2025-01-13T17:03:25Z")

</div>

Prefect thanks for the help the only thing that i noticed was it was setting some on the 3rd level of it at the same time but if you just add a

> selected = False

in the else it starts to work as intended. thanks for the help

```python
def recursiveSetHighlight(items, selection, parentSelected=False, idx=0):
		"""
		Recursively searches through a menu structure and highlights selected item and its parents
		"""
		SELECTED_CLASS = "Components/HorizontalMenu/HorizontalMenu_Selected"
		UNSELECTED_CLASS = "Components/HorizontalMenu/HorizontalMenu_Deselected"
		selected = False
		for itemIdx, item in enumerate(items):
			# Allow item highlight if this is first level of menu or
			# item index matches number at same level in selection list
			if (parentSelected or idx == 0) and idx < len(selection) and itemIdx == selection[idx]:
				item['style']['classes'] = SELECTED_CLASS
				selected = True
	
			# If this is not the item we want, unselect it
			else:
				item['style']['classes'] = UNSELECTED_CLASS
				selected = False
	
			# Apply Highlighting to child items
			if len(item['items']):
				item['items'] = recursiveSetHighlight(item['items'], selection, selected, idx+1)
	
		return items

```
