Note: This article is about a new method generally available as of June 2021, which replaces the older method. If you are already using SPA, you can find information on the previous method here.
Single-page applications (SPA) are becoming more and more popular in web development. Popular SPA frameworks include React, Vue, AngularJS, Ember, and Backbone. If your website is using one of these frameworks, read this article for the recommended implementation method.
Guidelines
With this solution, the implementation for single-page application is identical to non-single page applications. There is no need to implement an API call for SPA pageload, or mark which campaigns are relevant for these calls. Moreover, the solution is seamless to the framework.
How it works
The heavy lifting is done by Dynamic Yield:
- Dynamic Yield monitors loading of page elements, even when the page itself does not fully reload. This is done using a Web API called MutationObserver, which provides hooks into DOM mutations and enables Dynamic Yield to track when a DOM node is inserted, destroyed or modified. With that, Dynamic Yield applies (or reapplies) changes at the right moment. When the page deactivates, Dynamic Yield no longer attempts to change the elements on that page.
- The Dynamic Yield script has a state machine, and all of the campaigns are stateful smart objects. This way, the script responds to changes in your applications.
- Our elements are added through the Shadow DOM, in a way that doesn’t interfere with your application lifecycle.
Diagram of the current guidelines
To learn more about the difference between the old and the new guidelines, see the article about the old SPA guidelines.
Known limitations
- Older JavaScript libraries might not be supported. Note that these libraries might be in some Dynamic Yield templates (for example, Swiper is used for carousel experiences).
- The campaign experience CSS is not inherited from the site CSS, and therefore must be added explicitly in the variation code.
- A page refresh is required in order to assign users to CRM-based audiences.
- Setting the page context is mandatory.
Note: If auto-detect rules are enabled for a certain page type, there is no need to implement the page context on this page type as it simply will not be taken into account.
Page-change detection
The SPA solution uses multiple methods to detect changes to the page:
- URL changes are detected using the History API and Hashchange events. URL change detection ignores changes to URL query string parameters.
- Detection of the DY.recommendationContext object, which Dynamic Yield treats as a new page.
Note that no SPA event is sent unless the change in page URL also triggers a change in page context.
Campaign rendering
Dynamic Content and Recommendation campaigns are rendered into the Shadow DOM instead of the DOM directly. As a result, these campaigns do not interfere with the DOM change-detection of the front-end framework.
To enable seamless access to HTML elements of these campaigns, the JS code of the campaigns has multiple querying functions overridden, first querying the Shadow DOMs of the campaigns. If no elements are found, it queries the DOM as a fallback.
These functions are:
- document.getElementById
- document.querySelector
- document.querySelectorAll
- document.getElementsByName
- document.getElementsByClassName
- document.getElementsByTagName
Troubleshooting and FAQs
What is #shadow-root, and why is it visible in all of my Dynamic Yield campaigns running on the page?
#shadow-root (or Shadow DOM) is how Dynamic Yield injects content into SPA-enabled sites, and should be visible within any campaigns running on the page that are set to replace/add before/add after. This should be considered normal behavior for sites with SPA enabled.
Disabling the Serve on every SPA Event has no effect on the shadow-root itself. Shadow roots are implemented from your site when the SPA_FLOW feature flag is enabled.
Why is the CSS in my campaign not affecting the elements on my web page?
This behavior is also due to the Shadow DOM. Elements (and style tags) within the Shadow DOM are in a separate DOM instance from the rest of the web page.
If you are trying to manipulate objects running inside the Shadow DOM from outside, or vice versa, use JavaScript instead of CSS.
Targeting DOM from Shadow DOM (same syntax as no shadow DOM):
document.querySelector('#someElement').style.color = 'red';
Targeting Shadow DOM from DOM:
document.querySelector('div[data-dy-embedded-object]').shadowRoot.querySelector('#someElement').style.color = 'red'
Targeting Shadow DOM from separate Shadow DOM:
// same as "Targeting Shadow DOM from DOM", but may want to add a waitForElement for timing purposes
DYO.waitForElement('#shadow-root-container div[data-dy-embedded-object]', function() {
document.querySelector('#shadow-root-container div[data-dy-embedded-object]').shadowRoot.querySelector('#someElement').style.color = 'purple'
})
Note: jQuery can be used to update the styling, or toggle classes of elements running within the shadow DOM, using the same methods above but using $() syntax instead of a querySelector
Why are my external libraries not reflected in the campaign?
Currently, our external script feature adds external libraries to the header tag, and so, the external code is not part of the shadow DOM and therefore does not affect the internal campaign code.
You can either paste the code internally or programmatically import the code using the library URL.
Why does my SPA notification, overlay, or custom code campaign render on page load, but not when I navigate back to the page?
It's likely that the campaign is being loaded when the Dynamic Yield scripts are initially run. As you navigate away from the initial page load, there are no "refreshes" that occur when navigating an SPA, so the Dynamic Yield scripts are not being called to run again, and the campaign's insertion method is also not being run.
To fix this, in the campaign's Advanced Settings, toggle Serve on every SPA event.
Note that this is relevant only for notification, overlay, and custom code type campaigns.
SPA events cause campaigns to render again without needing to refresh the page.
For more information about what causes SPA events to fire, see the Page Change Detection section of this article.
Note: If the campaign's location is set to Add before or Add after, every SPA event will cause a duplicate campaign to be inserted above or below the previous one.
How can I verify that an SPA event was fired?
If you suspect your SPA events are not firing, or want to confirm they are working as expected, open your DevTools (or Web Developer Tool of choice), go to the Network tab, and filtering by "json?".
The network request is sent to the endpoint st.dynamicyield.com/spa/json.