Description
I have a Service Items Template with 2 collection lists (photos & videos) - this gets nested to the Service Template . I can see the combine is firing on the Service Items Template (photos are being nested into videos collection successfully here), but when its nested on the Service Template the combining doesn’t come through.
Service Item Template => Resource Integrated Ltd
Service Template => Content Strategy Services | Resource Integrated
I also have some custom code that hooks into the attributes API but I’m not sure if thats interfering with anything:
window.FinsweetAttributes = window.FinsweetAttributes || [];
window.FinsweetAttributes.push([
'list',
(listInstances) => {
listInstances.forEach((instance) => {
instance.addHook('afterRender', () => {
// Reinit GLightbox
const lightboxLinks = document.querySelectorAll(".glightbox");
if (lightboxLinks.length) {
if (window.glightboxInstance) {
window.glightboxInstance.destroy();
}
window.glightboxInstance = GLightbox({ selector: '.glightbox' });
}
// Vimeo handling
const vimeoIframes = document.querySelectorAll(
"iframe[src*='player.vimeo.com']");
vimeoIframes.forEach((iframe) => {
const wrapper = iframe.closest(".custom_vimeo-embed");
const player = new Vimeo.Player(iframe);
const btn = wrapper?.querySelector("button");
const playIcon = btn?.querySelector(".play");
const pauseIcon = btn?.querySelector(".pause");
if (!btn || !playIcon || !pauseIcon) return;
// Default: paused state
wrapper.classList.add("paused");
playIcon.style.display = "block";
pauseIcon.style.display = "none";
function setState(state) {
wrapper.classList.remove("playing", "paused");
wrapper.classList.add(state);
if (state === "playing") {
playIcon.style.display = "none";
pauseIcon.style.display = "block";
btn.setAttribute("aria-label", "Pause");
} else {
playIcon.style.display = "block";
pauseIcon.style.display = "none";
btn.setAttribute("aria-label", "Play");
}
}
player.on("loaded", () => player.pause().catch(() => {}));
player.on("play", () => setState("playing"));
player.on("pause", () => setState("paused"));
player.on("ended", async () => {
try {
await player.setCurrentTime(0);
await player.play();
} catch (e) {}
});
btn.addEventListener("click", (e) => {
e.stopPropagation();
player.getPaused().then((paused) =>
paused ? player.play() : player.pause()
);
});
player.getPaused().then((paused) =>
setState(paused ? "paused" : "playing")
);
});
});
});
}
]);
Site URL
Required: Please provide a staging/production URL where we can see the issue
Expected Behavior
The lists should combine, and the combined list should be nested onto the Service Template page OR
The lists should be nested onto the Service Template page THEN both lists should be combined
Actual Behavior
The lists are being combined on the Service Items Template, but when nested it does not combine.
Video/Screenshots
Required: Please provide a short screen recording showing the issue
Hey @hello51 !
We can see what’s happening with your List Combine + List Nest setup. The issue comes from the execution order in v2. When List Combine runs on the Service Items Template, that combined state doesn’t carry through once the lists are nested into the parent Service Template.
To fix this, the combine operation should happen after nesting, on the parent page:
On your Service Items Template: Set up the two source lists with the same instance (so they can later be combined).
Photos list: <div fs-list-element="list" fs-list-instance="media">
Videos list: <div fs-list-element="list" fs-list-instance="media">
On your Service Template (main page):
Keep your category list as is (fs-list-element="list"), with item-link pointing to the current service item.
Add a single nest-target div for the combined content:
<div fs-list-element="list" fs-list-combine="media">
<div fs-list-element="item">
<a fs-list-element="item-link" href="[Current Page]"></a>
<div fs-list-element="nest-target" fs-list-nest="media"></div>
</div>
</div>
This ensures that both lists are first nested within the parent and then combined into a single, clean list. That way, the combined output is stable and ready for any custom code you want to run afterwards.
Try this approach and let us know how it works for you! If you need further help with custom code implementation, @Support-Luis or @Support-Pedro can assist you.
Hi Finn,
Thanks for the reply!
I’ve tried implementing your solution, however it seems like the nest or combine is no longer running on either template.
Service Items Template => Resource Integrated Ltd
Service Template => Content Strategy Services | Resource Integrated
Are you able to review and verify my implementation?
CC: @Support-Luis @Support-Pedro
Thanks!
hello51
September 1, 2025, 1:23pm
4
Tried stripping my implementation down to the most basic, following the guide from the docs without combine and still cannot get at least list nest to work.
Really don’t know where I’m going wrong with this implementation
hello51
September 2, 2025, 1:04pm
5
Please let me know if you’re able to review my implementation and let me know where I’m going wrong with the nest + combine set up using attributes V2
@Support-Finn @Support-Luis @Support-Pedro
hello51
September 3, 2025, 12:26am
6
Was able to get the nest working for the images by placing:
fs-list-element=wrapper and fs-list-instanceon a div wrapping the link on the service item template page I assume cause I cant put the current link in a gallery collection list?
It appears that you cant nest 2 collections in a single nest target however as my videos are not being nested in the same media target as the images.
Service Template
Service Item Template
@Support-Luis @Support-Pedro
Hey @hello51 !
Could you try this snippet on your page?
<script>
window.FinsweetAttributes = window.FinsweetAttributes || [];
window.FinsweetAttributes.push([
'list',
async (listInstances) => {
const innerSelectors = [
'[fs-list-element="list"]',
'[role="list"]',
'.w-dyn-items',
'.w-dyn-list'
];
for (const listInstance of listInstances) {
const items = listInstance.items?.value || [];
await Promise.all(items.map(async (item) => {
try {
if (item.nesting && typeof item.nesting.then === 'function') {
await item.nesting;
}
const el = item.element;
if (!el) return;
const nestTargets = Array.from(
el.querySelectorAll('[fs-list-nest], [fs-list-element="nest-target"]')
);
if (nestTargets.length < 2) return;
const combinedTarget = nestTargets[0];
for (let j = 1; j < nestTargets.length; j++) {
const src = nestTargets[j];
let innerList = null;
for (const selector of innerSelectors) {
innerList = src.querySelector(selector);
if (innerList) break;
}
const rawNodes = innerList
? Array.from(innerList.children)
: Array.from(src.children);
const elementNodes = rawNodes.filter(node => node.nodeType === 1);
elementNodes.forEach(node => {
if (node.getAttribute && !node.getAttribute('role')) {
node.setAttribute('role', 'listitem');
}
combinedTarget.appendChild(node);
});
}
combinedTarget.setAttribute('fs-list-element', 'list');
combinedTarget.setAttribute('role', 'list');
} catch (err) {
console.error('❌ Error while merging nest-targets', err);
}
}));
}
}
]);
</script>
I made a local override, this was the result:
This script waits until the nested lists are ready. Then, for each item, it takes the first list as the main one and moves all items from the other lists into it. In the end, everything is together in one clean list.
Try this and please let me know how it goes!
hello51
September 10, 2025, 12:26am
8
Hi Pedro,
Thank you and @Support-Luis so much for helping me get this working, it’s really appreciated!
I was able to use your solution, and update it slightly to get the combined items in the same list to keep the grid layout cohesive
window.FinsweetAttributes = window.FinsweetAttributes || [];
window.FinsweetAttributes.push([
'list',
async (listInstances) => {
const innerSelectors = [
'[fs-list-element="list"]',
'[role="list"]',
'.w-dyn-items',
'.w-dyn-list'
];
for (const listInstance of listInstances) {
const items = listInstance.items?.value || [];
await Promise.all(items.map(async (item) => {
try {
if (item.nesting && typeof item.nesting.then === 'function') {
await item.nesting;
}
const el = item.element;
if (!el) return;
const nestTargets = Array.from(
el.querySelectorAll('[fs-list-nest], [fs-list-element="nest-target"]')
);
if (nestTargets.length < 2) return;
const combinedTarget = nestTargets[0];
const wrapper = combinedTarget.querySelector('.service_photo-list');
for (let j = 1; j < nestTargets.length; j++) {
const src = nestTargets[j];
let innerList = null;
for (const selector of innerSelectors) {
innerList = src.querySelector(selector);
if (innerList) break;
}
const rawNodes = innerList ?
Array.from(innerList.children) :
Array.from(src.children);
const elementNodes = rawNodes.filter(node => node.nodeType === 1);
elementNodes.forEach(node => {
if (node.getAttribute && !node.getAttribute('role')) {
node.setAttribute('role', 'listitem');
}
wrapper.prepend(node);
});
}
combinedTarget.setAttribute('fs-list-element', 'list');
combinedTarget.setAttribute('role', 'list');
initVimeoSetup();
initLightboxLinks();
} catch (err) {
console.error('❌ Error while merging nest-targets', err);
}
}));
}
}
]);
Thanks again!
1 Like