Conditional Logic to Replace Elfsight Form Submit Button

Hi,

I was wondering if it is possible to replace the default submit button in a Form Builder widget on my Squarespace website with different text and a link that takes them to another page on my website if they select “No” to the first question in my form?

Currently, regardless of whether the potential customer clicks Yes or No to the first question, it allows them to click “Get Your Free Quote” which effectively submits the form. If they select “No” I’d like the link to change to “Join Our Waiting List” and redirect them to: https://k9poopertroopers.com.au/waiting-list.

If required, a link to the form in question is here: https://k9poopertroopers.com.au/get-your-free-quote. Password is “K9PT”.

Thank you for any time and effort you spend on this—it is much appreciated.

Michael

2 Likes

Hi there, @Michael_Surnak :waving_hand:

I guess this customization is possible. Your request is with our devs now, and I’ll update you as soon as I have their response :slightly_smiling_face:

2 Likes

Thank you max, that would be very much appreciated.

2 Likes

Thank you for waiting!

Please add this script to the Custom JS field on the Settings tab of your widget’s settings and let me know how it worked:

const widgetSelector =
	'#eapps-form-builder-63498d7a-5c3d-4102-be49-ddfa279c560b';
const targetLabel =
	'Is your service address located within our current service area?';

const newButtonText = 'Join Our Waiting List';
const newButtonLink = 'https://k9poopertroopers.com.au/waiting-list';

const waitForElement = (selector, root = document) =>
	new Promise((res) => {
		const obs = new MutationObserver(() => {
			const el = root.querySelector(selector);
			if (el) {
				res(el);
				obs.disconnect();
			}
		});
		obs.observe(root, { childList: true, subtree: true });
	});

waitForElement(widgetSelector).then((container) => {
	let originalButtonText = null;

	function applyLogic() {
		const field = [
			...container.querySelectorAll('[class*="FormFieldLayout__Container-sc"]'),
		].find((f) => {
			const labelEl = f.querySelector('[class*="FormFieldLayout__Label-sc"]');
			if (!labelEl) return false;

			const labelText = labelEl.textContent?.trim().toLowerCase();
			return labelText?.startsWith(targetLabel.trim().toLowerCase());
		});

		if (!field) return;

		if (field.dataset.pooperInit === '1') return;
		field.dataset.pooperInit = '1';

		const buttonEl = container.querySelector('button[aria-label="Next"]');
		if (!buttonEl) return;

		const buttonLabel = buttonEl.querySelector(
			'[class*="ButtonBase__Ellipsis-sc"]'
		);
		if (!buttonLabel) return;

		if (!originalButtonText) originalButtonText = buttonLabel.textContent;

		const noInput = field.querySelector('input[value="No"]');
		const yesInput = field.querySelector('input[value="Yes"]');

		function updateButtonState() {
			if (noInput.checked) {
				buttonLabel.textContent = newButtonText;
				buttonEl.onclick = (e) => {
					e.preventDefault();
					e.stopPropagation();
					window.open(newButtonLink, '_blank');
				};
			} else {
				buttonLabel.textContent = originalButtonText;
				buttonEl.onclick = null;
			}
		}

		noInput.addEventListener('change', updateButtonState);
		yesInput?.addEventListener('change', updateButtonState);

		updateButtonState();
	}

	applyLogic();

	const obs = new MutationObserver(() => applyLogic());
	obs.observe(container, { childList: true, subtree: true });
});

Note: Custom JS doesn’t function in the preview mode, so you can check the result right on your website :slightly_smiling_face:

1 Like

Hi Max, I’ve added the code into the Custom JS field, validated it, and then published the form. I checked it on the website, and unfortunately nothing appears to have happened to the button, and the button also still submits the form as before and does not link to the Join Our Waiting List page.

I’ve left the custom JS in the form in case it assists the devs in troubleshooting.

Please let me know if there’s anything else I can do from my end.

2 Likes

I am so sorry about that!

Our devs will double-check the code. I’ll report back once I have their response :slightly_smiling_face:

2 Likes

Hi Max,

Logged on this morning and discovered that it’s working perfectly! Thank you to you and the dev team. Did the dev team do something in the background to make it work?

Looks like the JS code in the form is differen’t from the original that you sent through:

const widgetSelector =
#eapps-form-builder-63498d7a-5c3d-4102-be49-ddfa279c560b’;
const targetLabel =
‘Is your service address located within our current service area?’;

const newButtonText = ‘Join Our Waiting List’;
const newButtonLink = ‘https://k9poopertroopers.com.au/waiting-list’;

const originalTextMap = new WeakMap();
const patchedInputs = new WeakSet();
let buttonGlobalBound = false;

function patchStep(container) {
const field = […container.querySelectorAll(‘[class*=“FormFieldLayout__Container-sc”]’)]
.find(f => {
const labelEl = f.querySelector(‘[class*=“FormFieldLayout__Label-sc”]’);
return labelEl && labelEl.textContent.trim().toLowerCase()
.startsWith(targetLabel.toLowerCase());
});

if (!field) return;

const buttonEl = container.querySelector(‘button[aria-label=“Get Your Free Quote”]’);
if (!buttonEl) return;

const buttonLabel = buttonEl.querySelector(‘[class*=“ButtonBase__Ellipsis-sc”]’);
if (!buttonLabel) return;

if (!originalTextMap.has(buttonEl)) {
originalTextMap.set(buttonEl, buttonLabel.textContent);
}

const noInput = field.querySelector(‘input[value=“No”]’);
const yesInput = field.querySelector(‘input[value=“Yes”]’);
if (!noInput || !yesInput) return;

function updateButtonState() {
const shouldShowAlt = noInput.checked || (!noInput.checked && !yesInput.checked);

const getNewText = () => {
  if (shouldShowAlt) return newButtonText;
  if (buttonEl.hasAttribute('aria-label', 'Next')) return 'Next';
  return originalTextMap.get(buttonEl);
};

buttonLabel.textContent = getNewText();
buttonEl.dataset._waitingActive = shouldShowAlt ? "1" : "0";

}

[noInput, yesInput].forEach(input => {
if (!patchedInputs.has(input)) {
patchedInputs.add(input);
input.addEventListener(‘click’, () => setTimeout(updateButtonState, 0));
}
});

if (!buttonGlobalBound) {
buttonGlobalBound = true;
buttonEl.addEventListener(‘click’, (e) => {
if (buttonEl.dataset._waitingActive === “1”) {
e.preventDefault();
e.stopPropagation();
window.open(newButtonLink, ‘_blank’);
}
}, true);
}

updateButtonState();
}

const waitForWidget = setInterval(() => {
const container = document.querySelector(widgetSelector);
if (container) {
patchStep(container);
setInterval(() => patchStep(container), 300);
clearInterval(waitForWidget);
}
}, 300);

1 Like

Just an update for anyone following and for my own future reference:

I noticed the “Join Our Waiting List” button was already showing before any selection was made.

I’ve managed to fix this (with the assistance of ChatGPT) by changing this line:

const shouldShowAlt = noInput.checked || (!noInput.checked && !yesInput.checked);

to be:

const shouldShowAlt = noInput.checked;

3 Likes

Yep, as you’ve correctly noticed, devs adjusted the code, and I am happy to know it worked for you.

If anything else comes up, we’re always here to help :slightly_smiling_face:

2 Likes

Hello Max,

I have a follow-up request regarding a second form I’ve created, the “Change Request” form. The Change Request form can be accessed here. I’d like a similar behaviour to what was previously implemented on my first form:

  • When a customer selects “Just one” or “Multiple” for “How many service addresses would you like to add?”, or selects “Change a service address” for “What changes would you like to make to your service address/addresses?”,

  • The submit button should change its text to “Get Your Free Quote” and, when clicked, open the following link in a new tab: https://k9poopertroopers.com.au/get-your-free-quote.

I have been working with ChatGPT to modify the original JS code for the first form to achieve this. The solution worked partially: the button text correctly changes based on the selections. However, the issue is that if a user clicks back, the button text reverts as expected, but the functionality does not revert. Instead, it continues to open the link in a new tab, rather than behaving like the original submit button.

Would you be able to assist me in fixing the code so that when users navigate back, the buttons not only show the correct text but also restore their original behaviour? The draft, partially working code is below:

const widgetSelector = ‘#eapps-form-builder-17536663-c388-4d9c-900b-ac172987f487’;
const triggerFields = [
{
label: ‘What changes would you like to make to your service address/addresses?’,
values: [‘Change a service address’]
},
{
label: ‘How many service addresses would you like to add?’,
values: [‘Just one’, ‘Multiple’]
}
];
const newButtonText = ‘Get Your Free Quote’;
const newButtonLink = ‘https://k9poopertroopers.com.au/get-your-free-quote’;
const originalTextMap = new WeakMap();
const patchedInputs = new WeakSet();
let buttonGlobalBound = false;

function patchStep(container) {
const buttonEl = container.querySelector(‘button[aria-label=“Submit Change Request”]’);
if (!buttonEl) return;
const buttonLabel = buttonEl.querySelector(‘[class*=“ButtonBase__Ellipsis-sc”]’);
if (!buttonLabel) return;

if (!originalTextMap.has(buttonEl)) {
originalTextMap.set(buttonEl, buttonLabel.textContent);
}

// Function to update button state
function updateButtonState() {
let showAlt = false;
triggerFields.forEach(fieldInfo => {
const field = […container.querySelectorAll(‘[class*=“FormFieldLayout__Container-sc”]’)]
.find(f => {
const labelEl = f.querySelector(‘[class*=“FormFieldLayout__Label-sc”]’);
return labelEl && labelEl.textContent.trim().toLowerCase()
.startsWith(fieldInfo.label.toLowerCase());
});
if (!field) return;

  const inputs = [...field.querySelectorAll('input')].filter(i => fieldInfo.values.includes(i.value));
  if (inputs.some(i => i.checked)) showAlt = true;
});

const getNewText = () => showAlt ? newButtonText : originalTextMap.get(buttonEl);
buttonLabel.textContent = getNewText();
buttonEl.dataset._altActive = showAlt ? "1" : "0";

}

// Attach listeners to inputs
triggerFields.forEach(fieldInfo => {
const field = […container.querySelectorAll(‘[class*=“FormFieldLayout__Container-sc”]’)]
.find(f => {
const labelEl = f.querySelector(‘[class*=“FormFieldLayout__Label-sc”]’);
return labelEl && labelEl.textContent.trim().toLowerCase()
.startsWith(fieldInfo.label.toLowerCase());
});
if (!field) return;

const inputs = [...field.querySelectorAll('input')].filter(i => fieldInfo.values.includes(i.value));
inputs.forEach(input => {
  if (!patchedInputs.has(input)) {
    patchedInputs.add(input);
    input.addEventListener('click', () => setTimeout(updateButtonState, 0));
  }
});

});

if (!buttonGlobalBound) {
buttonGlobalBound = true;
buttonEl.addEventListener(‘click’, (e) => {
if (buttonEl.dataset._altActive === “1”) {
e.preventDefault();
e.stopPropagation();
window.open(newButtonLink, ‘_blank’);
}
}, true);
}

updateButtonState();
}

const waitForWidget = setInterval(() => {
const container = document.querySelector(widgetSelector);
if (container) {
patchStep(container);
setInterval(() => patchStep(container), 300);
clearInterval(waitForWidget);
}
}, 300);

Thank you in advance for any assistance you or your team provides.

Update: I’ve just noticed that the JavaScript code on both of my forms is no longer working. Could there have been a recent update on your end that might have affected its functionality?

Hi @Max,

Just following up on my message from 23 Nov to see if you have idea why the JavaScript code on both of my forms stopped working. Could there have been a recent update on Elfisghts end that might have affected its functionality?

1 Like

Hi there, @Michael_Surnak :waving_hand:

Unfortunately, your request from November 23 has been missed among others and I am truly sorry for all the inconvenience caused.

Our devs will check the script for the 1st widget, and I’ll update you once I have their response.

Regarding the form on this page, unfortunately, I couldn’t find the fields with these labels:

  • How many service addresses would you like to add?

  • What changes would you like to make to your service address/addresses?

If you’ve changed the labels, could you please share their new names and the number of the form pages where they are located?

Hi Max,

No problem at all and thank you!

The labels haven’t changed, the questions can be found in the form by navigating as follows:

Change my service address → Residential or Commercial (both work)

This then takes you to the following step which has those specific questions:

Kind regards,

Michael

1 Like

Found it — thanks!

You mentioned that the button text and link should change if “Change a service address” is selected for the first question, or if “Just One” / “Multiple” is selected for the second question.

With that logic, the “Change a service address” choice wouldn’t really affect the button, since the button would change for any selection in the second question anyway.

So just to confirm: you’d like the button text and link to change only when the “Change my service address” page ends up being the final step of the form — is that correct?

There are three instances where I’d like the button to be updated in this specific form. They are as follows:

Yes, all three of these occur on the “Change my service address” step of the form.

1 Like

Got it, thanks! I’ve passed it on to the devs and will report back once I have their response :slightly_smiling_face:

1 Like

Hi there, @Michael_Surnak :waving_hand:

Thank you for waiting!

Here is a new we’ve added to the widget installed on this page:

const widgetSelector =
  '.elfsight-app-63498d7a-5c3d-4102-be49-ddfa279c560b';
const targetLabel =
  'Is your service address located within our current service area?';
const newButtonText = 'Join Our Waiting List';
const newButtonLink = 'https://k9poopertroopers.com.au/waiting-list';
const originalTextMap = new WeakMap();
const patchedInputs = new WeakSet();
const boundButtons = new WeakSet();

function patchStep(container) {
  const field = [...container.querySelectorAll('[class*="FormFieldLayout__Container-sc"]')]
    .find(f => {
      const labelEl = f.querySelector('[class*="FormFieldLayout__Label-sc"]');
      return labelEl && labelEl.textContent.trim().toLowerCase()
        .startsWith(targetLabel.toLowerCase());
    });
  if (!field) return;

  const buttonEl = container.querySelector('button[aria-label="Get Your Free Quote"]');
  if (!buttonEl) return;

  const buttonLabel = buttonEl.querySelector('[class*="ButtonBase__Ellipsis-sc"]') || buttonEl;
  if (!originalTextMap.has(buttonEl)) {
    originalTextMap.set(buttonEl, buttonLabel.textContent);
  }

  const noInput = field.querySelector('input[value="No"]');
  const yesInput = field.querySelector('input[value="Yes"]');
  if (!noInput || !yesInput) return;

  function updateButtonState() {
    const shouldShowAlt = !!noInput.checked;

    const getNewText = () => {
      if (shouldShowAlt) return newButtonText;
      if (buttonEl.getAttribute('aria-label') === 'Next') return 'Next';
      return originalTextMap.get(buttonEl) ?? buttonLabel.textContent;
    };

    buttonLabel.textContent = getNewText();
    buttonEl.dataset._waitingActive = shouldShowAlt ? '1' : '0';
  }

  [noInput, yesInput].forEach(input => {
    if (!patchedInputs.has(input)) {
      patchedInputs.add(input);
      input.addEventListener('change', updateButtonState);
    }
  });

  if (!boundButtons.has(buttonEl)) {
    boundButtons.add(buttonEl);
    buttonEl.addEventListener('click', (e) => {
      if (buttonEl.dataset._waitingActive === '1') {
        e.preventDefault();
        e.stopPropagation();
        window.open(newButtonLink, '_blank');
      }
    }, true);
  }

  updateButtonState();
}

const waitForWidget = setInterval(() => {
  const container = document.querySelector(widgetSelector);
  if (container) {
    patchStep(container);
    setInterval(() => patchStep(container), 300);
    clearInterval(waitForWidget);
  }
}, 300);

And here is a script for the 2nd widget:

const widgetSelector =
  '.elfsight-app-17536663-c388-4d9c-900b-ac172987f487';
const targetLabel =
  'What changes would you like to make to your service address/addresses?';
const newButtonText = 'Get Your Free Quote';
const newButtonLink = 'https://k9poopertroopers.com.au/get-your-free-quote';
const originalTextMap = new WeakMap();
const patchedInputs = new WeakSet();
const boundButtons = new WeakSet();

function patchStep(container) {
  const field = [...container.querySelectorAll('[class*="FormFieldLayout__Container-sc"]')]
    .find(f => {
      const labelEl = f.querySelector('[class*="FormFieldLayout__Label-sc"]');
      return labelEl && labelEl.textContent.trim().toLowerCase()
        .startsWith(targetLabel.toLowerCase());
    });
  if (!field) return;

  const buttonEl = container.querySelector('button[aria-label="Submit Change Request"]');
  if (!buttonEl) return;

  const buttonLabel = buttonEl.querySelector('[class*="ButtonBase__Ellipsis-sc"]') || buttonEl;
  if (!originalTextMap.has(buttonEl)) {
    originalTextMap.set(buttonEl, buttonLabel.textContent);
  }

  const changeServiceInput = field.querySelector('input[value="Change a service address"]');
  if (!changeServiceInput) return;

  function updateButtonState() {
    const shouldShowAlt = changeServiceInput.checked;

    const getNewText = () => {
      if (shouldShowAlt) return newButtonText;
      if (buttonEl.getAttribute('aria-label') === 'Submit Change Request') return 'Submit Change Request';
      return originalTextMap.get(buttonEl) ?? buttonLabel.textContent;
    };

    buttonLabel.textContent = getNewText();
    buttonEl.dataset._waitingActive = shouldShowAlt ? '1' : '0';
  }

  
    if (!patchedInputs.has(changeServiceInput)) {
      patchedInputs.add(changeServiceInput);
      changeServiceInput.addEventListener('change', updateButtonState);
    }


  if (!boundButtons.has(buttonEl)) {
    boundButtons.add(buttonEl);
    buttonEl.addEventListener('click', (e) => {
      if (buttonEl.dataset._waitingActive === '1') {
        e.preventDefault();
        e.stopPropagation();
        window.open(newButtonLink, '_blank');
      }
    }, true);
  }

  updateButtonState();
}

const waitForWidget = setInterval(() => {
  const container = document.querySelector(widgetSelector);
  if (container) {
    patchStep(container);
    setInterval(() => patchStep(container), 300);
    clearInterval(waitForWidget);
  }
}, 300);

Please check them out and let me know how they work :slightly_smiling_face: