Shopify themes, liquid, logos, and UX
Hello,
I am trying to make my FAQ page easier for users by adding the accordion functionality as per this page:
https://github.com/mirceapiturca/Sections/tree/master/FAQ
For some reason only the top section expands/collapses while the lower sections don't.
Can anyone help me please?
This is the page:
https://www.humdinger.nz/pages/faqs
Cheers, Andrew
Solved! Go to the solution
This is an accepted solution.
@andylewie I've updated the GitHub code to support multiple instances. Please have a look at https://github.com/mirceapiturca/Sections/tree/master/FAQ
Here is a quick demo with 2 instances added on one page: https://machinelearning.myshopify.com/pages/faq?view=contact
There are also a few other features added:
- Expose additional max-width, margin-top, margin-bottom settings
- Toggle FAQ Rich Schema
Please let me know if you see any issues.
Thanks
I have figured out that the following code in the .js file is only returning the section ID for the first section, so the javascript never gets applied to the subsequent FAQ sections on that page.
How do I update this so it returns successive section IDs each time the .js file runs please?
function getConfig() {
return JSON.parse(document.querySelector('[data-faq-config]').innerHTML);
}
@Mircea_Piturca any tips for your code please?
Hi @andylewie ,
Sorry for the delay. Please let me have a look into this and I will get back to you today or tomorrow.
Thanks
Hi @andylewie,
It may have the same ID, that's why the code doesn't work.
Please send me the code you added, I will help you check it
Hi @LitCommerce , the code is in the following link:
https://github.com/mirceapiturca/Sections/tree/master/FAQ
The different sections all have unique IDs, but only the first ID is being returned as per the above post.
Cheers
This FAQ component was designed to only work for 1 instance, 1 section per page. This is because this component contains Google's FAQ rich snippet schema that can be only included once per page.
It should be possible to make it work on multiple sections per page. I will do my best to make this happened this week.
Cheers
Thanks a lot @Mircea_Piturca , your code is the best example I can find online so if it can be expanded across multiple sections that would be amazing. Having it integrated into the theme editor, including rich snippets, and having it relatively lightweight is much better than others I've experimented with.
Hi @andylewie,
Please follow the steps below:
- Step 1: Go to sections > faq.liquid file and remove code here:
- Step 2: Go to layout > theme.liquid file and add code above '</head>' tag.
Hope it helps!
@andylewie Please see @LitCommerce solution.
The faq.js needs to be included only once. Please make the suggested changes and let me know if all is OK.
Thank you
I had already tried that, but unfortunately it doesn't solve the issue.
In faq.js the getConfig() function still only returns the first FAQ section, so the subsequent FAQ sections are never being returned and hence never getting the javascript applied to them.
Hi @LitCommerce and @Mircea_Piturca , thank you for your help so far.
Hope the attached screenshots help to show what is happening, showing that I've got multiple FAQ sections separated by headings and the like.
Hi @andylewie,
You can make me a staff account, I will help you debug it
Hi @LitCommerce , I think I'm nearly there but thanks for the offer. I added a for loop at the top and a counter "faqIndex" which is really ugly coding, but is about the extent of my skills. Here is the updated faq.js:
(function FAQ(SD) {
'use strict';
var support = getSupport();
var faqIndex = 0;
for (faqIndex = 0; faqIndex < document.querySelectorAll('[data-faq-config]').length; faqIndex++) {
var config = getConfig();
var init = compose(publicAPI, setEvents, getBlocks, getConfig);
SD.faq = {}
SD.faq[config.sectionId] = init();
function publicAPI(config) {
return {
id: config.sectionId,
config: config,
blocks: zipObj(config.blockIds, config.blocks),
init: init
}
}
}
//**************************
/**
* Click event.
* @param {Object} block Section block elements and methods.
* @return {Object} Section block elements and methods.
*/
function blockEvents(block) {
block.trigger.addEventListener('click', function triggerClick() {
toggle(block);
});
return block;
}
/**
* Toggle block state.
* @param {Object} block Section block elements and methods.
* @return {Object} Section block elements and methods.
*/
function toggle(block) {
block.collapsed ? expand(block) : collapse(block);
return block;
}
/**
* Expand block.
* @param {Object} block Section block elements and methods.
* @return {Object} Section block elements and methods.
*/
function expand(block) {
block.button.setAttribute('aria-expanded', true);
block.panel.removeAttribute('hidden');
animate(block.panel, 'normal');
return block;
}
/**
* Collapse block.
* @param {Object} block Section block elements and methods.
* @return {Object} Section block elements and methods.
*/
function collapse(block) {
block.button.setAttribute('aria-expanded', false);
block.panel.setAttribute('hidden', '');
animate(block.panel, 'reverse');
return block;
}
/**
* Collapse block.
* @param {Object} block Section block elements and methods.
* @return {boolean} Collapsed block state.
*/
function isCollapsed(block) {
return Boolean(block.button.getAttribute('aria-expanded') === 'false');
}
/**
* Collapse block.
* @param {HTMLElement} element Block panel element to animate.
* @param {String} direction Animation direction, normal or reverse.
* @return {undefined} Nothing to return.
*/
function animate(element, direction) {
if (!support.WebAnimations) return;
element.setAttribute('data-is-animating', true);
element.animate([
{ height: 0 },
{ height: element.offsetHeight + 'px' }],
{ duration: 240,
fill: 'both',
easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
direction: direction
}
).onfinish = function() {
element.removeAttribute('data-is-animating');
this.cancel();
};
}
//**************************
/**
* Maps all block IDs to exposed block API.
* @param {Object} config Section and section blocks IDs.
* @return {Object} config Section and section blocks IDs.
*/
function getBlocks(config) {
config.blocks = config.blockIds.map(block);
return config;
}
/**
* Create section block API.
* @param {String} blockId Section Liquid block ID.
* @return {Object} block elements and methods.
*/
function block(blockId) {
return {
trigger: document.querySelector('[data-faq-trigger="' + blockId + '"]'),
button: document.querySelector('[data-faq-button="' + blockId + '"]'),
panel: document.querySelector('[data-faq-panel="' + blockId + '"]'),
get collapsed() { return isCollapsed(this) },
select: function select() { return expand(this) },
deselect: function deselect() { return collapse(this) }
}
}
/**
* Adds event listeners to block elements.
* @param {Object} config Section and section blocks IDs.
* @return {Object} config Section and section blocks IDs.
*/
function setEvents(config) {
config.blocks.forEach(blockEvents);
return config;
}
/**
* Pass the Liquid assigned section variabiles.
* @param {String} sectionId Current section ID.
* @return {Object} Section and section blocks IDs.
*/
function getConfig() {
return JSON.parse(document.querySelectorAll('[data-faq-config]').item(faqIndex).innerHTML);
}
/**
* Feature detection.
* @return {Object} Browser support.
*/
function getSupport() {
return {
WebAnimations: (typeof Element.prototype.animate === 'function')
}
}
//**************************
/**
* Creates a new object out of a list of keys and a list of values.
* Key/value pairing is truncated to the length of the shorter of the two lists.
* @example
* zipObj(['a', 'b', 'c'], [1, 2, 3]); //=> {a: 1, b: 2, c: 3}
* @param {Array} keys The array that will be properties on the output object.
* @param {Array} values The list of values on the output object.
* @return {Object} The object made by pairing up same-indexed elements of `keys` and `values`.
*/
function zipObj(keys, values) {
return keys.reduce(
function zipObj(acc, key, idx) {
acc[key] = values[idx];
return acc;
}, {}
)
}
/**
* Performs right-to-left function composition.
* The rightmost function may have any arity, the remaining functions must be unary.
* @example
* function plus1(n) {return n + 1};
* function plus2(n) {return n + 2};
* compose(plus2,plus1)(1) => 4
* @return {Function} Composed function
*/
function compose() {
var funcs = Array.prototype.slice.call(arguments).reverse();
return function() {
return funcs.slice(1).reduce(function(res, fn) {
return fn(res);
}, funcs[0].apply(undefined, arguments));
};
}
})(window.SectionsDesign = window.SectionsDesign || {});
Now I'll try figure out the rich data in faq.liquid
This is an accepted solution.
@andylewie I've updated the GitHub code to support multiple instances. Please have a look at https://github.com/mirceapiturca/Sections/tree/master/FAQ
Here is a quick demo with 2 instances added on one page: https://machinelearning.myshopify.com/pages/faq?view=contact
There are also a few other features added:
- Expose additional max-width, margin-top, margin-bottom settings
- Toggle FAQ Rich Schema
Please let me know if you see any issues.
Thanks
Works percetly, thank you very much!
Happy to hear that.
Feel free to submit bugs and improvements at https://github.com/mirceapiturca/Sections/tree/master/FAQ
Thanks
Hello @Mircea_Piturca, I have installed your FAQ into my Shopify store.
I am really happy with it overall, but I have 2 problems.
First problem, I can choose whichever color I want for the question and answer text, but it doesn't change anything. The question color remains black and the answer color remains dark grey.
The second problem is just with a iphone. When I want to open the FAQ page on my iphone, the question color and the "+" and "-" for dropdown are blue colored, which I can also not change. The answer color remains dark grey. And this just happens on a iphone, all other devices display black question color and dark grey answer color.
Do you happen to have a solution to this problems? I would be very grateful.
I am using the Refresh theme.
Hey Community! As we jump into 2025, we want to give a big shout-out to all of you wh...
By JasonH Jan 7, 2025Hey Community! As the holiday season unfolds, we want to extend heartfelt thanks to a...
By JasonH Dec 6, 2024Dropshipping, a high-growth, $226 billion-dollar industry, remains a highly dynamic bus...
By JasonH Nov 27, 2024