Karishma
Karishma

Reputation: 67

How to Generate Breadcrumb Schema Dynamically using Javascript?

Below is the HTML actually used to display the breadcrumbs on each page

            <c:forEach items="${breadcrumbArray}" var="breadcrumb" varStatus="loopCounter">
                <c:if test="${breadcrumb ne 'store'}">
                    <li class="breadcrumb__item">
                        <c:choose>
                            <c:when test="${not loopCounter.last}">                                    
                                <a href="/${breadcrumb}">${breadcrumb}</a>                             
                            </c:when>
                            <c:otherwise>
                                ${fn:replace(breadcrumb, '-', ' ')}
                            </c:otherwise>
                        </c:choose>
                    </li>
                </c:if>
            </c:forEach>

Javascript used to generate the schema dynamically

<script type="text/javascript">
    $(document).ready(function(){
       var el = document.createElement('script');

       el.type = 'application/ld+json';
       el.text = JSON.stringify({ "@context": "https://schema.org/",
                                  "@type": "BreadcrumbList",
                                  "itemListElement": [{
                                      "@type": "ListItem",
                                      "position": 1,
                                      "name": "",
                                      "item": ""                                     
                                  }]
                             });

       document.querySelector('head').appendChild(el);
    });
</script>

The schema for example for " Home > Our Mission > Who We Are " should be generated like:

<script type="application/ld+json">
{
    "@context": "https://schema.org/",
    "@type": "BreadcrumbList",
    "itemListElement": [{
        "@type": "ListItem",
        "position": 1,
        "name": "Home",
        "item": "https://corp.com/"
        },{
        "@type": "ListItem",
        "position": 2,
        "name": "Our Mission",
        "item": "https://corp.com/our-mission/"
        },{
        "@type": "ListItem",
        "position": 3,
        "name": "Who We Are",
        "item": "https://corp.com/our-mission/who-we-are"
    }]
}
</script>

The script is not getting appended to the 'head' tag ..

Upvotes: 1

Views: 2987

Answers (2)

Karishma
Karishma

Reputation: 67

I found that we don't need javascript for dynamically populating the values. HTML5 works well. Below is the final script that i am using to render the schema

P.S. The business requirement was to use json-ld only and not rdfa

<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "BreadcrumbList",
"itemListElement": [
    {
    "@type": "ListItem",
    "position": 1
    "name": "Home",
    "item": "${baseURL}",
    },
    <c:forEach items="${breadcrumbArray}" var="breadcrumb" varStatus="loopCounter">
    {
    "@type": "ListItem",
    "position": ${loopCounter.index+1}
    "name": "${fn:replace(breadcrumb, '-', ' ')}",
    "item": "${baseURL}${breadcrumbArray[loopCounter.index-1]}/${breadcrumb}",
    }
    </c:forEach>
    ]
}
</script>

Upvotes: 0

Stu Furlong
Stu Furlong

Reputation: 3629

There are a few pieces to this one.

First: Script not being appended to `head` tag

This might require more information, what you have in your code should at least render the JSON-LD script tag in your head. There might be something going on in your environment preventing you from writing to head. You could try changing your code to something like:

document.head.appendChild(el) or document.getElementsByTagName('head')[0].appendChild(el) (If you need to support older browsers).

More info on that can be found at these SO threads:

Again, I have doubts that this is the issue. I'd also try rendering the script somewhere else on the page instead of head to see if that works for you (also related, see my final note at the end of this answer).

Second: Creating the JSON-LD with JavaScript

Here's a rough, jQuery free but ES5 compatable solution to how you could generate this:

HTML

<ol class="breadcrumbs">
    <li class="breadcrumb-item">
        <a class="breadcrumb-link" href="https://mypage.com/page1">Page 1</a>
    </li>
    <li class="breadcrumb-item">
        <a class="breadcrumb-link" href="https://mypage.com/page2">Page 2</a>
    </li>
    <li class="breadcrumb-item">
        <a class="breadcrumb-link" href="https://mypage.com/page3">Page 3</a>
    </li>
</ol>
<!-- Testing Purposes -->
<div class="output"></div>

JavaScript

// Create Script
var el = document.createElement('script');
el.type = 'application/ld+json';

// Set initial position
var position = 0;

// Create breadcrumb object
var breadcrumb = {
    position:0,
    name:"",
    item:""
}

// Empty array for list items
var listArray = []

// Loop through each breadcrumb link and set attributes
var items = document.querySelectorAll('.breadcrumb-link');
for(var i = 0; i < items.length; i++) {
    var newItem = Object.create(breadcrumb);
    var curItem = items[i];

    newItem["@type"] = "ListItem";
    position++;
    newItem.position = position;
    newItem.name = curItem.text;
    newItem.item = curItem.getAttribute('href');
    listArray.push(newItem);
}

// Create overarching Schema object
var breadcrumbSchema = {
    "@context": "https://schema.org/",
    "@type": "BreadcrumbList",
    "itemListElement": listArray
};

// Stringify JSON
var finalSchema = JSON.stringify(breadcrumbSchema);

// Add schema to Script
el.text = finalSchema;

// Set head variable with browser fallback
var head = document.head || document.getElementsByTagName("head")[0];

// Add to head (This won't work in codepen)
head.appendChild(el);

// Testing purposes - Show example of string in HTML
document.querySelector('.output').innerHTML = finalSchema;

// Testing purposes - Inspect source to see script generated inside of the "output" div
document.querySelector('.output').appendChild(el);

Here's an example CodePen

Third: Do you actually want to do it this way?

As a commenter mentioned writing out JSON-LD with JavaScript could be a problem. If your intentions for this are SEO related you might want to play the safe bet and render this server side. There's no guarantee search engine bots are catching something like this rendered on the client side.

Update: It looks like Google will play nicely with JSON-LD rendered dynamically, but this still may be a problem with other search engines. I also like to err on the side of caution. Thanks @JayGray

If you have to hardcode values for JSON-LD, as the commenter mentioned you could use RDFa which is still supported (even if not preferred compared to JSON-LD). Based on your code the HTML is rendered dynamically. Here's an example of what that looks like from the BreadcrumbList on schema.org:

<ol vocab="https://schema.org/" typeof="BreadcrumbList">
    <li property="itemListElement" typeof="ListItem">
        <a property="item" typeof="WebPage" href="https://example.com/dresses">
            <span property="name">Dresses</span>
        </a>
        <meta property="position" content="1">
    </li>
    <li property="itemListElement" typeof="ListItem">
        <a property="item" typeof="WebPage" href="https://example.com/dresses/real">
            <span property="name">Real Dresses</span>
        </a>
        <meta property="position" content="2">
    </li>
</ol>

(Keep in mind that RDFa and Microdata - unlike JSON-LD - will not get picked up by search engines if generated dynamically).

Final Note:

Even though JSON-LD is recommended to be in head, it does NOT need to be in order to be picked up properly (at least with Google). It can really be anywhere in your content and it will work as well.

Additionally if you run your page through the Structured Data Testing tool - any JSON-LD in the body will still validate fine. I assume the Rich Snippets tool would also validate but I haven't confirmed that yet.

Upvotes: 3

Related Questions