8

I wrote a dynamic component to build toast messages based on ApexPages.Message in my answer here, but one aspect of it has been bothering me ever since, and I'm trying to fix it. When the toast is rendered, its icon doesn't display. It doesn't seem to be an error with the markup, since if I inspect the element and modify the source to remove it then add it back in verbatim, the icon then renders. The exact same markup also works fine if rendered on page load.

Here is an MVR:

<apex:page showHeader="false" standardStylesheets="false" sidebar="false"
           applyHtmlTag="false" applyBodyTag="false" docType="html-5.0">
    <html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" lang="en">
        <head>
            <title>Demo Toast</title>
            <apex:slds />
        </head>
        <body>
            <div class="slds-scope">
                <div id="target" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                    <div id="messageOnPageLoad" class="slds-notify slds-notify--alert slds-theme--alert-texture slds-theme--warning" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" role="alert">
                        <span class="slds-icon_container">
                            <svg class="slds-icon slds-icon-text-default slds-icon--small slds-m-right--x-small" aria-hidden="true">
                                <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#info"></use>
                            </svg>
                        </span>
                        <h2><span>Here is a toast message!</span></h2>
                    </div>
                </div>
            </div>
        </body>
    </html>
    <script>
        (function (D, w) {
            "use strict";
            var targetDiv = document.getElementById("target");
            w.buildToast = function (message) {
                var toast = targetDiv.appendChild(D.createElement("div"));
                toast.className = "slds-notify slds-notify--alert slds-theme--alert-texture slds-theme--warning";
                toast.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
                toast.setAttribute("xmlns", "http://www.w3.org/2000/svg");
                toast.setAttribute("role", "alert");

                var icon = toast.appendChild(D.createElement("span"));
                icon.className = "slds-icon_container";

                var svg = icon.appendChild(D.createElement("svg"));
                svg.className = "slds-icon slds-icon-text-default slds-icon--small slds-m-right--x-small";
                svg.setAttribute("aria-hidden", true);

                var use = svg.appendChild(D.createElement("use"));
                use.setAttribute("xlink:href", "/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#info");

                toast.appendChild(D.createElement("h2"))
                    .appendChild(D.createElement("span"))
                    .appendChild(D.createTextNode("Here is a toast message!"));
            };
            D.addEventListener("DOMContentLoaded", w.buildToast);
        }(document, window));
    </script>
</apex:page>

Why are my dynamically rendered toast icons not displaying properly?

Toasts

I'm using Chrome 62 on Mac, but I don't think it's worked on any Chrome version I've installed.

3
  • does the context matter? I copy/pasted this markup into a new VF page and hit Quick Save, then the preview button and the icon was shown as expected. Chrome 63, classic interface.
    – Mark Pond
    Dec 19, 2017 at 17:19
  • @MarkPond It might, I haven't tried preview. I'm using Chrome 62 on Mac.
    – Adrian Larson
    Dec 19, 2017 at 17:20
  • 1
    My apologies - I saw line 1 with the icon and thought "looks fine!". My experience is the same as your screenshot above, no icon on the 2nd, dynamic, line.
    – Mark Pond
    Dec 19, 2017 at 17:25

1 Answer 1

7

This isn't a Lightning problem, but a generic element namespace problem. You have to create the element in the correct "namespace" for this to work. Credit to this question for pointing out the problem.

Basically, the SVG and USE elements need to be created in the SVG namespace using the createElementNS methods. Also, some of the attributes had to be set via setAttributeNS in order for it to work correctly. I may have slightly overkilled, but the version below appears to be identical.

<apex:page showHeader="false" standardStylesheets="false" sidebar="false"
           applyHtmlTag="false" applyBodyTag="false" docType="html-5.0">
    <html lang="en">
        <head>
            <title>Demo Toast</title>
            <apex:slds />
        </head>
        <body>
            <div class="slds-scope">
                <div id="target" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                    <div id="messageOnPageLoad" class="slds-notify slds-notify--alert slds-theme--alert-texture slds-theme--warning" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" role="alert">
                        <span class="slds-icon_container">
                            <svg class="slds-icon slds-icon-text-default slds-icon--small slds-m-right--x-small" aria-hidden="true">
                                <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#info"></use>
                            </svg>
                        </span>
                        <h2><span>Here is a toast message!</span></h2>
                    </div>
                </div>
            </div>
        </body>
    </html>
    <script>
        (function (D, w) {
            "use strict";
            var targetDiv = document.getElementById("target");
            w.buildToast = function (message) {
                var toast = targetDiv.appendChild(D.createElement("div"));
                toast.className = "slds-notify slds-notify--alert slds-theme--alert-texture slds-theme--warning";
                toast.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
                toast.setAttribute("xmlns", "http://www.w3.org/2000/svg");
                toast.setAttribute("role", "alert");

                var icon = toast.appendChild(D.createElement("span"));
                icon.className = "slds-icon_container";

                var svg = icon.appendChild(document.createElementNS("http://www.w3.org/2000/svg","svg"));
                svg.setAttribute("class","slds-icon slds-icon-text-default slds-icon--small slds-m-right--x-small");
                svg.setAttribute("aria-hidden", true);

                var use = svg.appendChild(D.createElementNS("http://www.w3.org/2000/svg","use"));
                use.setAttributeNS("http://www.w3.org/1999/xlink", "href", "/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#info");

                toast.appendChild(D.createElement("h2"))
                    .appendChild(D.createElement("span"))
                    .appendChild(D.createTextNode("Here is a toast message!"));
            };
            D.addEventListener("DOMContentLoaded", w.buildToast);
        }(document, window));
    </script>
</apex:page>
4
  • 4
    I was about 50% done crafting nearly the exact same response, including the link to the other answer about createElementNS(). Excellent!
    – Mark Pond
    Dec 19, 2017 at 17:48
  • The only NS call which seems unnecessary is the attribute specification. Three where two will do is hardly "overkill"!
    – Adrian Larson
    Dec 19, 2017 at 17:50
  • Hmm, maybe it is necessary on the attribute too.
    – Adrian Larson
    Dec 19, 2017 at 18:05
  • @AdrianLarson Yeah, it didn't work for me either without it, but I wasn't sure if there was a way around it.
    – sfdcfox
    Dec 19, 2017 at 18:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.