Querying Reflow classes in Javascript returns empty list

Hello,

I am trying to reference the Reflow classes in a javascript file but am getting empty node lists. For example when trying to get all of the product names which use the class .ref-name:

const reflowNames = document.querySelectorAll(".ref-name");
console.log(reflowNames);

I always get an empty list. In the browser’s inspector I see that the product name has that class, and also in BSS, but I’m not sure why the script can’t see it. Are user scripts loaded before the page inserts the Reflow data? If this is the case is there a way to defer the script until after the page with all the Reflow elements has loaded?

P.S. - Thanks for such an awesome product! I love the simplicity and vanilla-like feel. I know how to code a website from scratch but this make things so much easier and faster. Keep up the great work.

I think you might be correct about the Reflow data. Since Reflow inserts all the data using JS it’s likely it does this after your script runs.

You can try using the window.onload function to wait till all of the page has finished loading. It might work.

window.onload = function() {
      ...
};

Thanks! Doesn’t seem to be working though.

I can maybe put a timer to sleep a bit before querying the Reflow classes but I need them to render the content differently, and a flash of the old content wouldn’t be a good user experience I think.

Any other ideas?

document.addEventListener('reflow-ready', function() {
  const reflowNames = document.querySelectorAll(".ref-name");
 console.log(reflowNames);
});

Edit: It seems even with the reflow-ready it still needs time to load so try this code instead:

document.addEventListener('reflow-ready', function() {
    setTimeout(() => {

        const elements = document.querySelectorAll(".ref-name");
        elements.forEach(element => {
            console.log(element.textContent);  
        });

    }, 100);
});
1 Like

I had to increase the timeout to 500ms for when I did a hard refresh, and there’s a flash of the old content before new content appears, but otherwise this works. Thanks!

You can use the reflow api to get the product names
add your store id to get the product names from your store

Good idea. Eventually I do want to use the api to make the store more unique and tailored to our needs, but BSS’s default Reflow components are so easy to just drop in and they just work. The above solution will work for now until Reflow introduces multi-language functionality, or I dive into the api and make everything custom.

You could try this::

function waitForElement(selector) {
  return new Promise(resolve => {
    if (document.querySelector(selector)) {
      return resolve(document.querySelector(selector));
    }

    const observer = new MutationObserver(mutations => {
      if (document.querySelector(selector)) {
        resolve(document.querySelector(selector));
        observer.disconnect();
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  });
}

document.addEventListener('DOMContentLoaded', function() {
  const refNameElm = document.querySelectorAll('.ref-name');

  waitForElement('.ref-name').then((elm) => {
    const elements = document.querySelectorAll(".ref-name");
    elements.forEach((element) => {
      elementContent = element.textContent;
      console.log(element.textContent);

      if (elementContent.includes("wood")) {
        element.classList.add("wood");
      } else {
        element.classList.add("normal");
      }
    });
  }
   );
})

CSS Set to hide (opacity) the ref-name to avoid any flash, and then the javascript adds the class(wood) to any title with the word “wood” (you will see in the example)

.ref-name {
  opacity: 0;/*hide on initial load*/
  transition: opacity 500ms;/*half second transition*/
}

.wood {/*if the title contains the word wood then show red*/
  opacity: 1;
  color: red;
}

.normal {/* otherwise show green*/
  opacity: 1;
  color: green;
}

The example below :
https://round-bird-4667.bss.design/

You can translate reflow to your language

Very nice! This works much better. Thanks @richards. Here is how I implemented it. All of my products have both translations in the name, for example “Toy|Juguete”. The script then saves it to a dataset and chooses which to show based on the current language selected.

(EDIT: Switched innerText to textContent as richards shows, seems to be better for this use case).

const translationClasses = [
    ".translation",
    ".ref-name",
    ".ref-category",
    ".ref-excerpt"
];

function waitForElement(selector) {
	return new Promise(resolve => {
		if(document.querySelector(selector)) {
			return resolve(document.querySelector(selector));
		}

		const observer = new MutationObserver(mutations => {
			if(document.querySelector(selector)) {
				resolve(document.querySelector(selector));
				observer.disconnect();
			}
		});

		observer.observe(document.body, {
			childList: true,
			subtree: true
		});
	});
}

document.addEventListener('DOMContentLoaded', function() {
	// const translations = document.querySelectorAll('.ref-name');
	if(!Cookies.get('language')) {
		Cookies.set('language', '0', { expires: 30, sameSite: 'lax' });
	}
	translationClasses.forEach((c) => {
		waitForElement(c).then((elm) => {
			const elements = document.querySelectorAll(c);
			elements.forEach((element) => {
				if(element.textContent.includes("|")) {
	                element.dataset.translations = element.textContent;
					translate(element, parseInt(Cookies.get('language')));
	            }
				element.classList.add('show');
			});
		});
	});
});

function translate(e, lang) {
    if(e.dataset.translations) {
        e.textContent= e.dataset.translations.split('|')[lang];
    }
}

And the CSS like you suggested:

.translation, .ref-name, .ref-category, .ref-excerpt {
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

.translation.show, .ref-name.show, .ref-category.show, .ref-excerpt.show {
  opacity: 1;
}

Thanks for the help.

1 Like

Thanks for the tip. This translates the Reflow general text, like “Shopping Cart”, but does not give me the ability to translate my actual product names and descriptions. Also I believe I would have to create two separate sites, one for each localization json file. The way I have implemented allows me to maintain only one site and to easily enter both languages into my page.

I also know there is a way to translate the whole page using Google Translate but I would much rather have control over how the page is translated.