Create an Interactive Shopping Mall Map With JavaScript & SVG

Most of us have probably used one of these interactive shopping mall maps to search for stores in our local shopping mall. They provide a quick overview of what stores are available, where the store is located in the shopping mall and provide information regarding opening hours etc etc.

I’ll be creating a basic version of this shopping mall map today, primarily focusing on interacting with each store unit. So, when a user clicks into a store on the map, a pop-up box will appear and display the name, description and opening hours of the store.

1. Creating Our Shopping Mall SVG File

Before we begin coding up our floor map, I do need to address the fact that you should be comfortable enough creating basic vector shapes in a vector graphics program if you decide to create a custom map for your own project. I won’t be covering this aspect as it falls outside the scope of the tutorial.

Right – so with that out of the way, I’ve created the design for this tutorial’s shopping mall plan in Adobe Illustrator. You can also create your custom floor plan in Illustrator or use one of the many alternative apps out there.

Once I was happy with the overall design of my interactive shopping mall map, I exported the design as an SVG and made sure to convert all of text to outlines.

2. Creating the Main Files of Our Interactive Shopping Mall Map

We will be using four files to create our little interactive map:

  • index.html: The map that will hold the SVG code / units.
  • style.css: Styling for our page / map, units, and hover states etc.
  • unit-data.js: JS object that will contain the information for each unit.
  • main.js: Where the magic happens.
  • index.html
  • style.css
  • unit-data.js
  • main.js
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" type="text/css" href="style.css">
        <title>Interactive Shopping Mall Map</title>
  </head>
<body>

    <div id="feh-mall-wrapper">
        <h1>Shopping Center Map</h1>
        <!-- SVG Code will be placed here -->
    </div>
    
    <div id="unit-popup">
        <button id="unit-popup-close">&times;</button>
        <div id="unit-content"></div>
    </div>
  
    <script src="js/unit-data.js"></script>
    <script src="js/main.js"></script>
  
</body>
</html>
* {
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}
html,
body {
    font-family: arial, sans-serif;
    color: #565656;
    height: 100%;
}
body {
    display: flex;
    align-items: center;
}

#feh-mall-wrapper {
    border: 5px solid #ccc;
    margin: 0 auto 0 auto;
    width: 800px;
}
// this will hold all of the store's info
// functionality 

And we have a nice clean area set up and ready to work on.

Notes

  • Please note that the SVG floor plan code has not yet been added to the index.html file to ensure clarity across all sections and files.
  • In the CSS file, I’ve cleaned up things by resetting the paddings / margins, added a font family and centered the map.
  • index.html contains two important elements #feh-mall-wrapper and #unit-popup.
  • #feh-mall-wrapper will hold our page heading and our SVG code.

3. Embedding Our SVG Code

Now, with my exported SVG file, I can simply open it up in my code editor to view the source code, and paste that into the #feh-mall-wrapper div.

Note: I have edited the exported SVG for a number of reasons:

  • To make the code more readable and organised, as Adobe Illustrator spits it out in a pretty sorry state.
  • I have grouped all of the different coloured units by wrapping them in a <g> tag with associated ID <g id="units-blue"></div>. This will be useful when we begin to style the map and add interactivity styles.
  • I have added a numbered ID for each unit (rect, polygon etc). This will be needed when we add JavaScript a bit further on.
  • Given each unit a .unit class, to easier colour each… well, unit 🙂
  • The units in most shopping malls will usually never be all leased out at one time, some will be closed, so I have added a .unit-unavailable class to a few random units.
  • I have also given each number container (<g>) two classes, a generic .num class to colour, and the number of the associated unit: <g class="num num-01"></div>.
  • index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" type="text/css" href="style.css">
        <title>Interactive Shopping Mall Map Demo</title>
    </head>
<body>

    <div id="feh-mall-wrapper">

        <h1>Interactive Shopping Mall Map</h1>

        <svg id="mall-map" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 2432 1763">
            <g id="units-blue">
                <polygon id="unit_01" class="unit" points="0 0 202.24 0 348.09 154.91 0 154.91 0 0"/>
                <rect id="unit_02" class="unit unit-unavailable" y="188.5" width="348.16" height="203.37"/>
                <rect id="unit_03" class="unit" x=".02" y="424.15" width="348.09" height="277.69"/>
                <rect id="unit_04" class="unit" x=".04" y="735.26" width="286.14" height="131.73"/>
                <rect id="unit_05" class="unit" x=".03" y="900.98" width="348.16" height="131.73"/>
                <rect id="unit_06" class="unit" y="1064.7" width="348.09" height="277.69"/>
                <rect id="unit_07" class="unit" y="1375.23" width="286.14" height="200.85"/>
                <polygon id="unit_08" class="unit" points="0 1763 166.24 1763 286.14 1608.09 0 1608.09 0 1763"/>
                
                <g class="num num-01">
                    <path d="M136.93,87.08c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM119.88,87.56c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M150.07,75.68h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                </g>
                <g class="num num-02">
                    <path d="M135.34,288.33c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM118.29,288.81c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M139.25,304.37v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                </g>
                <g class="num num-03">
                    <path d="M135.32,561.14c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM118.27,561.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M140.25,572.3c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                </g>
                <g class="num num-04">
                    <path d="M135.32,800.14c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM118.27,800.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M152.38,816.18v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM152.38,804.29v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                </g>
                <g class="num num-05">
                    <path d="M135.32,965.14c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM118.27,965.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M157.98,953.35h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.13-4.88,10.72-11.69,10.72-3.43,0-6.33-.97-7.83-1.93l1.06-3.24c1.3.77,3.86,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.58Z"/>
                </g>
                <g class="num num-06">
                    <path d="M137.32,1202.14c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM120.27,1202.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M159.16,1189.77c-.87-.05-1.98,0-3.19.19-6.67,1.11-10.19,5.99-10.92,11.16h.15c1.5-1.98,4.11-3.62,7.58-3.62,5.56,0,9.47,4.01,9.47,10.15,0,5.75-3.91,11.06-10.44,11.06s-11.11-5.22-11.11-13.38c0-6.18,2.22-11.06,5.31-14.15,2.61-2.56,6.09-4.16,10.05-4.64,1.26-.19,2.32-.24,3.09-.24v3.48ZM157.95,1207.89c0-4.49-2.56-7.2-6.47-7.2-2.56,0-4.93,1.59-6.09,3.87-.29.48-.48,1.11-.48,1.88.1,5.17,2.46,8.99,6.91,8.99,3.67,0,6.14-3.04,6.14-7.54Z"/>
                </g>
                <g class="num num-07">
                    <path d="M137.32,1471.14c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM120.27,1471.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M161.62,1455.77v2.8l-13.67,28.6h-4.4l13.62-27.78v-.1h-15.36v-3.53h19.81Z"/>
                </g>
                <g class="num num-08">
                    <path d="M137.32,1686.14c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM120.27,1686.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M140.85,1694.2c0-3.96,2.37-6.76,6.23-8.41l-.05-.15c-3.48-1.64-4.98-4.35-4.98-7.05,0-4.98,4.2-8.36,9.71-8.36,6.09,0,9.13,3.82,9.13,7.73,0,2.66-1.3,5.51-5.17,7.34v.15c3.91,1.55,6.33,4.3,6.33,8.12,0,5.46-4.69,9.13-10.68,9.13-6.57,0-10.53-3.91-10.53-8.5ZM157.76,1694.01c0-3.82-2.66-5.65-6.91-6.86-3.67,1.06-5.65,3.48-5.65,6.47-.15,3.19,2.27,5.99,6.28,5.99s6.28-2.37,6.28-5.6ZM146.07,1678.31c0,3.14,2.37,4.83,5.99,5.8,2.71-.92,4.78-2.85,4.78-5.7,0-2.51-1.5-5.12-5.31-5.12-3.53,0-5.46,2.32-5.46,5.02Z"/>
                </g>
            </g>

            <g id="units-green">
                <polygon id="unit_09" class="unit" points="470 318.31 603 318.31 692.83 432.11 470 432.11 470 318.31"/>
                <rect id="unit_10" class="unit" x="470" y="463.19" width="222.84" height="113.81"/>
                <polygon id="unit_11" class="unit" points="724 463.19 857 463.19 946.84 577 724 577 724 463.19"/>
                <rect id="unit_12" class="unit" x="470" y="608.19" width="222.84" height="113.81"/>
                <rect id="unit_13" class="unit" x="724" y="608.19" width="222.84" height="113.81"/>
                <rect id="unit_14" class="unit unit-unavailable" x="470" y="753.19" width="222.84" height="227.81"/>
                <rect id="unit_15" class="unit" x="470" y="1012.19" width="222.84" height="205.81"/>
                <rect id="unit_16" class="unit" x="470" y="1249.19" width="222.84" height="113.81"/>
                <rect id="unit_17" class="unit" x="724" y="753.19" width="222.84" height="464.81"/>
                <rect id="unit_18" class="unit" x="724" y="1249.19" width="222.84" height="113.81"/>
                <rect id="unit_19" class="unit" x="470" y="1394.19" width="95" height="95.81"/>
                <rect id="unit_20" class="unit" x="596" y="1394.19" width="95" height="95.81"/>
                <rect id="unit_21" class="unit" x="470" y="1521" width="95" height="97"/>
                <rect id="unit_22" class="unit unit-unavailable" x="596" y="1521" width="95" height="97"/>
                <circle id="unit_23" class="unit" cx="835" cy="1506" r="112"/>
                <rect id="unit_24" class="unit" x="470" y="1649.19" width="464" height="113.81"/>

                <g class="num num_09">
                    <path d="M558.32,379.14c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM541.27,379.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                    <path d="M564.7,392.18c.92.1,1.98,0,3.43-.15,2.46-.34,4.78-1.35,6.57-3.04,2.08-1.88,3.57-4.64,4.15-8.36h-.15c-1.74,2.13-4.25,3.38-7.39,3.38-5.65,0-9.28-4.25-9.28-9.61,0-5.94,4.3-11.16,10.73-11.16s10.39,5.22,10.39,13.24c0,6.91-2.32,11.74-5.41,14.73-2.42,2.37-5.75,3.82-9.13,4.2-1.55.24-2.9.29-3.91.24v-3.48ZM566.25,374.11c0,3.91,2.37,6.67,6.04,6.67,2.85,0,5.07-1.4,6.18-3.29.24-.39.39-.87.39-1.55,0-5.36-1.98-9.47-6.43-9.47-3.62,0-6.18,3.19-6.18,7.63Z"/>
                </g>
                <g class="num num_10">
                    <path d="M561.68,512.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M598.11,524.14c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM581.05,524.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                </g>
                <g class="num num_11">
                    <path d="M808.68,512.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M833.46,512.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                </g>
                <g class="num num_12">
                    <path d="M561.68,657.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M577.24,685.18v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                </g>
                <g class="num num_13">
                    <path d="M813.68,657.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M830.25,680.3c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                </g>
                <g class="num num_14">
                    <path d="M561.68,858.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M590.38,886.18v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM590.38,874.29v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                </g>
                <g class="num num_15">
                    <path d="M561.68,1104.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M595.98,1104.35h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.13-4.88,10.72-11.69,10.72-3.43,0-6.33-.97-7.83-1.93l1.06-3.24c1.3.77,3.86,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.58Z"/>
                </g>
                <g class="num num_16">
                    <path d="M561.68,1296.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M595.16,1295.77c-.87-.05-1.98,0-3.19.19-6.67,1.11-10.19,5.99-10.92,11.16h.15c1.5-1.98,4.11-3.62,7.58-3.62,5.56,0,9.47,4.01,9.47,10.15,0,5.75-3.91,11.06-10.44,11.06s-11.11-5.22-11.11-13.38c0-6.18,2.22-11.06,5.31-14.15,2.61-2.56,6.09-4.16,10.05-4.64,1.26-.19,2.32-.24,3.09-.24v3.48ZM593.95,1313.89c0-4.49-2.56-7.2-6.47-7.2-2.56,0-4.93,1.59-6.09,3.87-.29.48-.48,1.11-.48,1.88.1,5.17,2.46,8.99,6.91,8.99,3.67,0,6.14-3.04,6.14-7.54Z"/>
                </g>
                <g class="num num_17">
                    <path d="M808.68,975.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M844.62,971.77v2.8l-13.67,28.6h-4.4l13.62-27.78v-.1h-15.36v-3.53h19.81Z"/>
                </g>
                <g class="num num_18">
                    <path d="M813.68,1295.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M828.85,1315.2c0-3.96,2.37-6.76,6.23-8.41l-.05-.15c-3.48-1.64-4.98-4.35-4.98-7.05,0-4.98,4.2-8.36,9.71-8.36,6.09,0,9.13,3.82,9.13,7.73,0,2.66-1.3,5.51-5.17,7.34v.15c3.91,1.55,6.33,4.3,6.33,8.12,0,5.46-4.69,9.13-10.68,9.13-6.57,0-10.53-3.91-10.53-8.5ZM845.76,1315.01c0-3.82-2.66-5.65-6.91-6.86-3.67,1.06-5.65,3.48-5.65,6.47-.15,3.19,2.27,5.99,6.28,5.99s6.28-2.37,6.28-5.6ZM834.07,1299.31c0,3.14,2.37,4.83,5.99,5.8,2.71-.92,4.78-2.85,4.78-5.7,0-2.51-1.5-5.12-5.31-5.12-3.53,0-5.46,2.32-5.46,5.02Z"/>
                </g>
                <g class="num num_19">
                    <path d="M500.42,1432.17h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                    <path d="M518.44,1456.62c.92.1,1.98,0,3.43-.15,2.46-.34,4.78-1.35,6.57-3.04,2.08-1.88,3.57-4.64,4.15-8.36h-.15c-1.74,2.12-4.25,3.38-7.39,3.38-5.65,0-9.28-4.25-9.28-9.61,0-5.94,4.3-11.16,10.73-11.16s10.39,5.22,10.39,13.24c0,6.91-2.32,11.74-5.41,14.73-2.42,2.37-5.75,3.82-9.13,4.2-1.55.24-2.9.29-3.91.24v-3.48ZM519.99,1438.55c0,3.91,2.37,6.67,6.04,6.67,2.85,0,5.07-1.4,6.18-3.29.24-.39.39-.87.39-1.55,0-5.36-1.98-9.47-6.43-9.47-3.62,0-6.18,3.19-6.18,7.63Z"/>
                </g>
                <g class="num num_20">
                    <path d="M618.2,1459.61v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.42,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M663.85,1443.57c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM646.8,1444.06c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                </g>
                <g class="num num_21">
                    <path d="M491.2,1586.61v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.42,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M525.21,1559.17h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                </g>
                <g class="num num_22">
                    <path d="M618.2,1586.61v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.42,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M642.98,1586.61v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.42,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                </g>
                <g class="num num_23">
                    <path d="M812.39,1521.18v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.42,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M838.19,1516.3c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                </g>
                <g class="num num_24">
                    <path d="M673.45,1729.84v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.55-6.13-6.23-6.13-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.83,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M711.38,1729.84v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM711.38,1717.95v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                </g>
            </g>

            <g id="units-yellow">
                <rect id="unit_25" class="unit" x="522" width="198" height="177"/>
                <polygon id="unit_26" class="unit" points="1044.73 0 1044.73 263 844.47 263 750.97 169.5 750.97 0 1044.73 0"/>
                <rect id="unit_27" class="unit" x="1075.7" width="263" height="330"/>
                <rect id="unit_28" class="unit" x="1367" width="141" height="171"/>
                <rect id="unit_29" class="unit" x="1365" y="202" width="141" height="128"/>
                <rect id="unit_30" class="unit unit-unavailable" x="1535.75" y="0" width="492.06" height="330.07"/>
                <rect id="unit_31" class="unit" x="2055.03" width="173" height="263"/>
                <rect id="unit_32" class="unit" x="2259" width="173" height="263"/>

                <g class="num num_25">
                    <path d="M592.2,103.8v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M635.72,75.97h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.14-4.88,10.72-11.69,10.72-3.43,0-6.33-.97-7.83-1.93l1.06-3.24c1.3.77,3.87,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.57Z"/>
                </g>
                <g class="num num_26">
                    <path d="M874.2,154.8v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M916.9,126.39c-.87-.05-1.98,0-3.19.19-6.67,1.11-10.19,5.99-10.92,11.16h.15c1.5-1.98,4.11-3.62,7.58-3.62,5.56,0,9.47,4.01,9.47,10.15,0,5.75-3.91,11.06-10.43,11.06s-11.11-5.22-11.11-13.38c0-6.18,2.22-11.06,5.31-14.15,2.61-2.56,6.09-4.15,10.05-4.64,1.26-.19,2.32-.24,3.09-.24v3.48ZM915.7,144.51c0-4.49-2.56-7.2-6.47-7.2-2.56,0-4.93,1.59-6.09,3.86-.29.48-.48,1.11-.48,1.88.1,5.17,2.46,8.99,6.91,8.99,3.67,0,6.14-3.04,6.14-7.54Z"/>
                </g>
                <g class="num num_27">
                    <path d="M1190.2,188.8v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M1235.37,157.4v2.8l-13.67,28.6h-4.4l13.62-27.78v-.1h-15.36v-3.53h19.81Z"/>
                </g>
                <g class="num num_28">
                    <path d="M1411.71,103.61v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.54-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M1436.1,95.64c0-3.96,2.37-6.76,6.23-8.41l-.05-.15c-3.48-1.64-4.98-4.35-4.98-7.05,0-4.98,4.2-8.36,9.71-8.36,6.09,0,9.13,3.82,9.13,7.73,0,2.66-1.3,5.51-5.17,7.34v.15c3.91,1.55,6.33,4.3,6.33,8.12,0,5.46-4.69,9.13-10.68,9.13-6.57,0-10.53-3.91-10.53-8.5ZM1453.01,95.44c0-3.82-2.66-5.65-6.91-6.86-3.67,1.06-5.65,3.48-5.65,6.47-.14,3.19,2.27,5.99,6.28,5.99s6.28-2.37,6.28-5.6ZM1441.32,79.74c0,3.14,2.37,4.83,5.99,5.8,2.71-.92,4.78-2.85,4.78-5.7,0-2.51-1.5-5.12-5.31-5.12-3.53,0-5.46,2.32-5.46,5.02Z"/>
                </g>
                <g class="num num_29">
                    <path d="M1411.71,283.61v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.54-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                    <path d="M1438.95,280.61c.92.1,1.98,0,3.43-.15,2.46-.34,4.78-1.35,6.57-3.04,2.08-1.88,3.58-4.64,4.16-8.36h-.15c-1.74,2.13-4.25,3.38-7.39,3.38-5.65,0-9.28-4.25-9.28-9.61,0-5.94,4.3-11.16,10.72-11.16s10.39,5.22,10.39,13.24c0,6.91-2.32,11.74-5.41,14.73-2.42,2.37-5.75,3.82-9.13,4.2-1.55.24-2.9.29-3.91.24v-3.48ZM1440.5,262.55c0,3.91,2.37,6.67,6.04,6.67,2.85,0,5.07-1.4,6.18-3.29.24-.39.39-.87.39-1.55,0-5.36-1.98-9.47-6.43-9.47-3.62,0-6.18,3.19-6.18,7.63Z"/>
                </g>
                <g class="num num_30">
                    <path d="M1760.19,174.34c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.87.77,7.01,3.67,7.01,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.17-2.13l1.16-3.29Z"/>
                    <path d="M1804.83,163.18c0,10.68-3.96,16.57-10.92,16.57-6.13,0-10.29-5.75-10.39-16.14,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM1787.78,163.66c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                </g>
                <g class="num num_31">
                    <path d="M2122.36,151.64c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.87.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                    <path d="M2155.36,129.08h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                </g>
                <g class="num num_32">
                    <path d="M2318.36,151.64c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                    <path d="M2342.13,156.52v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.84,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                </g>
            </g>
            
            <g id="units-red">
                <polygon id="unit_33" class="unit" points="1069.41 452 1373.71 452 1373.71 668.66 1265.53 769.81 1069.41 769.81 1069.41 452"/>
                <polygon id="unit_34" class="unit" points="1713.88 452 1409.58 452 1409.58 668.66 1517.76 769.81 1713.88 769.81 1713.88 452"/>
                <rect id="unit_35" class="unit" x="1749.34" y="452.41" width="203.64" height="316.98"/>
                <rect id="unit_36" class="unit" x="1988.85" y="452.41" width="203.64" height="316.98"/>
                <rect id="unit_37" class="unit" x="2228.36" y="452.41" width="203.64" height="190.41"/>
                <polygon id="unit_38" class="unit unit-unavailable" points="1069 1010.65 1196.28 1121 1373.71 1120.33 1373.71 903.67 1265.53 802.52 1069.41 802.52 1069 1010.65"/>
                <polygon id="unit_39" class="unit" points="1713.88 1120.33 1409.58 1120.33 1409.58 903.67 1517.76 802.52 1713.88 802.52 1713.88 1120.33"/>
                <rect id="unit_40" class="unit" x="1749.34" y="802.93" width="203.64" height="316.98"/>
                <rect id="unit_41" class="unit" x="1988.85" y="802.93" width="203.64" height="151.46"/>
                <rect id="unit_42" class="unit" x="1988.85" y="987.93" width="203.64" height="131.99" rx="61" ry="61"/>
                <rect id="unit_43" class="unit unit-unavailable" x="2228.36" y="674.19" width="203.64" height="190.41"/>
                <rect id="unit_44" class="unit" x="2228.36" y="898.14" width="203.64" height="221.78"/>

                <g class="num num_33">
                    <path d="M1182.62,617.8c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                    <path d="M1207.4,617.8c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                </g>
                <g class="num num_34">
                    <path d="M1538.62,617.8c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.87.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                    <path d="M1575.53,622.68v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM1575.53,610.8v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                </g>
                <g class="num num_35">
                    <path d="M1822.62,617.8c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.87.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                    <path d="M1865.13,594.85h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.14-4.88,10.72-11.69,10.72-3.43,0-6.33-.97-7.83-1.93l1.06-3.24c1.3.77,3.86,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.57Z"/>
                </g>
                <g class="num num_36">
                    <path d="M2068.62,617.8c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.87.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                    <path d="M2110.31,594.27c-.87-.05-1.98,0-3.19.19-6.67,1.11-10.19,5.99-10.92,11.16h.14c1.5-1.98,4.11-3.62,7.58-3.62,5.56,0,9.47,4.01,9.47,10.15,0,5.75-3.91,11.06-10.44,11.06s-11.11-5.22-11.11-13.38c0-6.18,2.22-11.06,5.31-14.15,2.61-2.56,6.09-4.15,10.05-4.64,1.26-.19,2.32-.24,3.09-.24v3.48ZM2109.11,612.39c0-4.49-2.56-7.2-6.47-7.2-2.56,0-4.93,1.59-6.09,3.86-.29.48-.48,1.11-.48,1.88.1,5.17,2.46,8.99,6.91,8.99,3.67,0,6.14-3.04,6.14-7.54Z"/>
                </g>
                <g class="num num_37">
                    <path d="M2305.29,566.8c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.13l1.16-3.29Z"/>
                    <path d="M2349.44,540.28v2.8l-13.67,28.6h-4.4l13.62-27.78v-.1h-15.36v-3.53h19.81Z"/>
                </g>
                <g class="num num_38">
                    <path d="M1184.29,981.8c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.16-2.12l1.16-3.29Z"/>
                    <path d="M1207.67,978.71c0-3.96,2.37-6.76,6.23-8.41l-.05-.14c-3.48-1.64-4.98-4.35-4.98-7.05,0-4.98,4.2-8.36,9.71-8.36,6.09,0,9.13,3.82,9.13,7.73,0,2.66-1.3,5.51-5.17,7.34v.14c3.91,1.55,6.33,4.3,6.33,8.12,0,5.46-4.69,9.13-10.68,9.13-6.57,0-10.53-3.91-10.53-8.5ZM1224.58,978.52c0-3.82-2.66-5.65-6.91-6.86-3.67,1.06-5.65,3.48-5.65,6.47-.15,3.19,2.27,5.99,6.28,5.99s6.28-2.37,6.28-5.6ZM1212.89,962.82c0,3.14,2.37,4.83,5.99,5.8,2.71-.92,4.78-2.85,4.78-5.7,0-2.51-1.5-5.12-5.31-5.12-3.53,0-5.46,2.32-5.46,5.02Z"/>
                </g>
                <g class="num num_39">
                    <path d="M1539.29,981.8c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.17-2.12l1.16-3.29Z"/>
                    <path d="M1565.52,983.68c.92.1,1.98,0,3.43-.14,2.46-.34,4.78-1.35,6.57-3.04,2.08-1.88,3.58-4.64,4.16-8.36h-.15c-1.74,2.13-4.25,3.38-7.39,3.38-5.65,0-9.28-4.25-9.28-9.61,0-5.94,4.3-11.16,10.73-11.16s10.39,5.22,10.39,13.24c0,6.91-2.32,11.74-5.41,14.73-2.42,2.37-5.75,3.82-9.13,4.2-1.55.24-2.9.29-3.91.24v-3.48ZM1567.07,965.62c0,3.91,2.37,6.67,6.04,6.67,2.85,0,5.07-1.4,6.18-3.29.24-.39.39-.87.39-1.55,0-5.36-1.98-9.47-6.43-9.47-3.62,0-6.18,3.19-6.18,7.63Z"/>
                </g>
                <g class="num num_40">
                    <path d="M1836.41,986.68v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM1836.41,974.8v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M1868.93,970.64c0,10.68-3.96,16.57-10.92,16.57-6.14,0-10.29-5.75-10.39-16.13,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM1851.87,971.12c0,8.17,2.51,12.8,6.38,12.8,4.35,0,6.43-5.07,6.43-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.43,4.54-6.43,13.09Z"/>
                </g>
                <g class="num num_41">
                    <path d="M2082.41,897.68v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM2082.41,885.8v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M2103.28,870.24h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                </g>
                <g class="num num_42">
                    <path d="M2082.41,1072.68v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM2082.41,1060.8v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M2094.06,1072.68v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.43,0-3.19-1.55-6.13-6.23-6.13-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.83,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                </g>
                <g class="num num_43">
                    <path d="M2319.08,793.85v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM2319.08,781.96v-10.48c0-1.64.05-3.29.14-4.93h-.14c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M2331.74,788.97c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.17-2.13l1.16-3.29Z"/>
                </g>
                <g class="num num_44">
                    <path d="M2319.08,1027.85v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM2319.08,1015.96v-10.48c0-1.64.05-3.29.14-4.93h-.14c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M2343.86,1027.85v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM2343.86,1015.96v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                </g>
            </g>

            <g id="units-turquoise">
                <polygon id="unit_45" class="unit" points="1069 1342.8 1357.85 1342.8 1357.85 1243 1152.62 1243 1069 1342.8"/>
                <rect id="unit_46" class="unit" x="1069" y="1376.41" width="288.85" height="99.8"/>
                <rect id="unit_47" class="unit unit-unavailable" x="1069" y="1508.78" width="288.85" height="99.8"/>
                <rect id="unit_48" class="unit" x="1069" y="1641.14" width="288.85" height="121.86"/>
                <rect id="unit_49" class="unit unit-unavailable" x="1391.1" y="1243" width="130.89" height="520"/>
                <rect id="unit_50" class="unit" x="1554.19" y="1243" width="110.12" height="195.39"/>
                <rect id="unit_51" class="unit" x="1694.43" y="1243" width="140.24" height="195.39"/>
                <rect id="unit_52" class="unit" x="1866.88" y="1243" width="110.12" height="195.39"/>
                <rect id="unit_53" class="unit" x="1551.04" y="1470.96" width="422.8" height="292.04"/>
                <rect id="unit_54" class="unit" x="2009.2" y="1243" width="422.8" height="520"/>
            
                <g class="num num_45">
                    <path d="M1205.08,1305.68v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM1205.08,1293.8v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M1235.47,1277.85h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.14-4.88,10.72-11.69,10.72-3.43,0-6.33-.96-7.83-1.93l1.06-3.24c1.3.77,3.86,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.57Z"/>
                </g>
                <g class="num num_46">
                    <path d="M1205.08,1440.68v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM1205.08,1428.8v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M1234.65,1412.27c-.87-.05-1.98,0-3.19.19-6.67,1.11-10.19,5.99-10.92,11.16h.15c1.5-1.98,4.11-3.62,7.58-3.62,5.56,0,9.47,4.01,9.47,10.14,0,5.75-3.91,11.06-10.44,11.06s-11.11-5.22-11.11-13.38c0-6.18,2.22-11.06,5.31-14.15,2.61-2.56,6.09-4.16,10.05-4.64,1.26-.19,2.32-.24,3.09-.24v3.48ZM1233.44,1430.39c0-4.49-2.56-7.2-6.47-7.2-2.56,0-4.93,1.59-6.09,3.86-.29.48-.48,1.11-.48,1.88.1,5.17,2.46,8.99,6.91,8.99,3.67,0,6.14-3.04,6.14-7.54Z"/>
                </g>
                <g class="num num_47">
                    <path d="M1205.08,1571.68v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM1205.08,1559.8v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M1237.11,1540.28v2.8l-13.67,28.6h-4.4l13.62-27.78v-.1h-15.36v-3.53h19.81Z"/>
                </g>
                <g class="num num_48">
                    <path d="M1205.08,1716.68v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM1205.08,1704.8v-10.48c0-1.64.05-3.29.15-4.93h-.15c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M1216.34,1708.71c0-3.96,2.37-6.76,6.23-8.41l-.05-.14c-3.48-1.64-4.98-4.35-4.98-7.05,0-4.98,4.2-8.36,9.71-8.36,6.09,0,9.13,3.82,9.13,7.73,0,2.66-1.3,5.51-5.17,7.34v.14c3.91,1.55,6.33,4.3,6.33,8.12,0,5.46-4.69,9.13-10.68,9.13-6.57,0-10.53-3.91-10.53-8.5ZM1233.25,1708.52c0-3.82-2.66-5.65-6.91-6.86-3.67,1.06-5.65,3.48-5.65,6.47-.15,3.19,2.27,5.99,6.28,5.99s6.28-2.37,6.28-5.6ZM1221.55,1692.82c0,3.14,2.37,4.83,5.99,5.8,2.71-.92,4.78-2.85,4.78-5.7,0-2.51-1.5-5.12-5.31-5.12-3.53,0-5.46,2.32-5.46,5.02Z"/>
                </g>
                <g class="num num_49">
                    <path d="M1447.08,1517.18v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM1447.08,1505.3v-10.48c0-1.64.05-3.29.14-4.93h-.14c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                    <path d="M1461.18,1514.19c.92.1,1.98,0,3.43-.15,2.46-.34,4.78-1.35,6.57-3.04,2.08-1.88,3.57-4.64,4.15-8.36h-.14c-1.74,2.13-4.25,3.38-7.39,3.38-5.65,0-9.28-4.25-9.28-9.61,0-5.94,4.3-11.16,10.72-11.16s10.39,5.22,10.39,13.24c0,6.91-2.32,11.74-5.41,14.73-2.42,2.37-5.75,3.82-9.13,4.2-1.55.24-2.9.29-3.91.24v-3.48ZM1462.73,1496.12c0,3.91,2.37,6.67,6.04,6.67,2.85,0,5.07-1.4,6.18-3.29.24-.39.39-.87.39-1.55,0-5.36-1.98-9.47-6.42-9.47-3.62,0-6.18,3.19-6.18,7.63Z"/>
                </g>
                <g class="num num_50">
                    <path d="M1605.68,1338.35h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.14-4.88,10.72-11.69,10.72-3.43,0-6.33-.96-7.83-1.93l1.06-3.24c1.3.77,3.87,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.57Z"/>
                    <path d="M1632.59,1350.14c0,10.68-3.96,16.57-10.92,16.57-6.13,0-10.29-5.75-10.39-16.13,0-10.53,4.54-16.33,10.92-16.33s10.39,5.89,10.39,15.89ZM1615.54,1350.62c0,8.16,2.51,12.8,6.38,12.8,4.35,0,6.42-5.07,6.42-13.09s-1.98-12.8-6.38-12.8c-3.72,0-6.42,4.54-6.42,13.09Z"/>
                </g>
                <g class="num num_51">
                    <path d="M1759.68,1338.35h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.14-4.88,10.72-11.69,10.72-3.43,0-6.33-.96-7.83-1.93l1.06-3.24c1.3.77,3.87,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.57Z"/>
                    <path d="M1774.95,1338.74h-.1l-5.46,2.95-.82-3.24,6.86-3.67h3.62v31.4h-4.11v-27.44Z"/>
                </g>
                <g class="num num_52">
                    <path d="M1916.68,1338.35h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.14-4.88,10.72-11.69,10.72-3.43,0-6.33-.96-7.83-1.93l1.06-3.24c1.3.77,3.87,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.57Z"/>
                    <path d="M1922.72,1366.18v-2.61l3.33-3.24c8.02-7.63,11.64-11.69,11.69-16.42,0-3.19-1.55-6.14-6.23-6.14-2.85,0-5.22,1.45-6.67,2.66l-1.35-3c2.17-1.83,5.27-3.19,8.89-3.19,6.76,0,9.61,4.64,9.61,9.13,0,5.8-4.2,10.48-10.82,16.86l-2.51,2.32v.1h14.11v3.53h-20.05Z"/>
                </g>
                <g class="num num_53">
                    <path d="M1758.57,1603.33h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.13-4.88,10.72-11.69,10.72-3.43,0-6.33-.97-7.83-1.93l1.06-3.24c1.3.77,3.87,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.02v3.58Z"/>
                    <path d="M1765.62,1626.28c1.21.77,4.01,1.98,6.96,1.98,5.46,0,7.15-3.48,7.1-6.09-.05-4.4-4.01-6.28-8.12-6.28h-2.37v-3.19h2.37c3.09,0,7-1.59,7-5.31,0-2.51-1.59-4.73-5.51-4.73-2.51,0-4.93,1.11-6.28,2.08l-1.11-3.09c1.64-1.21,4.83-2.42,8.21-2.42,6.18,0,8.99,3.67,8.99,7.49,0,3.24-1.93,5.99-5.8,7.39v.1c3.86.77,7,3.67,7,8.07,0,5.02-3.91,9.42-11.45,9.42-3.53,0-6.62-1.11-8.17-2.13l1.16-3.29Z"/>
                </g>
                <g class="num num_54">
                    <path d="M2215.21,1489.35h-11.98l-1.21,8.07c.72-.1,1.4-.19,2.56-.19,2.42,0,4.83.53,6.76,1.69,2.46,1.4,4.49,4.11,4.49,8.07,0,6.14-4.88,10.72-11.69,10.72-3.43,0-6.33-.96-7.83-1.93l1.06-3.24c1.3.77,3.86,1.74,6.71,1.74,4.01,0,7.44-2.61,7.44-6.81-.05-4.06-2.75-6.96-9.03-6.96-1.79,0-3.19.19-4.35.34l2.03-15.07h15.03v3.57Z"/>
                    <path d="M2234.39,1517.18v-8.55h-14.59v-2.8l14.01-20.05h4.59v19.52h4.4v3.33h-4.4v8.55h-4.01ZM2234.39,1505.3v-10.48c0-1.64.05-3.29.14-4.93h-.14c-.97,1.84-1.74,3.19-2.61,4.64l-7.68,10.68v.1h10.29Z"/>
                </g>
            </g>
        </svg>
    </div>

    <div id="unit-popup">
        <button id="unit-popup-close">&times;</button>
        <div id="unit-content"></div>
    </div>
  
    <script src="js/unit-data.js"></script>
    <script src="js/main.js"></script>
  
</body>
</html> 

And we are left with the following:

The structure’s there but why is everything black!?

Don’t worry – the colours and numbers have all vanished as we have edited the SVG code, and added in our own specific IDs and classes. We will create new styles in the next section and re-add the colours back in!

4. Styling / Colouring the SVG Floor Plan

The very first thing I’ll do here is create a generic .unit-unavailable class that can be applied to any unit in our floor plan. It will reduce the opacity by half and change the cursor upon hover, indicating that the unit is unavailable.

.unit-unavailable {
   opacity: 0.5;
   cursor: not-allowed;
}

And while we’re at it, we can add a ‘hand’ cursor for all units.

 .unit {
   cursor: pointer;
}

Now I can add the colours back into each unit by targeting the .unit and .num elements within their respective parent #units–colour IDs, and use the fill property to apply the associated unit colours.

I’ll have to create three styles for each of the five grouped units (blue, green, yellow, red and turquoise). There will be the default colour, a hovered colour and then a darker colour for the number ‘text’.

4.1. Blue Units Example

Here are all of the styles for all of our blue units.

  • style.css
#units-blue .unit { 
   fill: #75bfe7; 
}
#units-blue .unit:not(.unit-unavailable):hover { 
   fill: #13A2E2; 
}
#units-blue .num { 
   fill: #003c56; 
}

Here’s a quick visual mock-up:

Notes:

I’ve added a :not pseudo class, so when we hover over each blue unit and if a unit is unavailable, there will be no change in the colour.

4.2. Adding Colour to the Rest of the Store Units

With the blue units looking… blue again, let’s re-add in the colours for the rest of the floor plan’s stores.

  • style.css
#units-blue .unit { 
   fill: #75bfe7; 
}
#units-blue .unit:not(.unit-unavailable):hover { 
   fill: #13A2E2; 
}
#units-blue .num { 
   fill: #003c56; 
}
            
#units-green .unit { 
   fill: #a4c354; 
}
#units-green .unit:not(.unit-unavailable):hover {
   fill: #8aa538;
}
#units-green .num {
   fill: #340;
}

#units-yellow .unit { 
   fill: #fae769; 
}
#units-yellow .unit:not(.unit-unavailable):hover {
   fill: #ddc654;
}
#units-yellow .num {
   fill: #5e4e00;
}

#units-red .unit {
   fill: #b7514f;
}
#units-red .unit:not(.unit-unavailable):hover {
   fill: #9e3e3e;
}
#units-red .num {
   fill: #230100;
}

#units-turquoise .unit {
   fill: #56c1b1;
}
#units-turquoise .unit:not(.unit-unavailable):hover {
   fill: #42a090;
}
#units-turquoise .num {
   fill: #003d33;
}

And we’re now back in business 🔥

One last thing to do is to center align the heading, give it a background colour, give both the heading and SVG some padding, and tighten up the width.

  • style.css
#feh-mall-wrapper {
   ...

    h1 {
        background-color: #ccc;
        padding: 15px;
        text-align: center;
    }

    svg#mall-map {
        width: auto;
        max-width: 800px;
        height: auto;
        display: block;
        padding: 15px;
    }

And we have a nice and clean interactive shopping mall map.

5. Styling  the Unit Pop-Up Box

Now, it’s time to style the pop-up box, so when we click on a store / unit  – a little pop-up box (modal) will appear, displaying each unit’s store details. (I will style the inner text elements later on, when we will work on this part).

  • style.css
/** 
 * Unit pop-up
 */
#unit-popup {
   position: absolute;
   background: #fff;
   padding: 15px;
   border-radius: 5px;
   width: 300px;
   height: 175px;
   box-shadow: 2px 2px 10px rgba(0,0,0,0.2);
   /* display: none; */
}
   #unit-popup-close {
      position: absolute;
      top: 0px;
      right: 0px;
      cursor: pointer;
      font-weight: bold;
      width: 30px;
      height: 30px;
      border: 0px none;
      font-size: 20px;
      border-radius: 0 5px;
   }
   #unit-content {
      padding-right: 25px;
   }

And now we are left with this nice modal pop-up box .

Notes:

  • The pop-up box is absolutely positioned, and will appear on the map ‘floated’ to the left hand side. This is fine for the moment, as we will make it open near to the unit that is clicked, shortly.
  • The close button obviously won’t work just yet too 😀
  • You should now uncomment /* display: none; */ from the #unit-popup in style.css

6. Populating Store Information

I think at this point, we now need some data to populate our stores with!

We can see from the floor plan that we have 54 units in total, so I have asked Chat GPT to help me create store names, along with brief descriptions for all of these units.

For those curious – here is the prompt I used:

Could you please generate 54 fake units/shops with unit names starting from unit_01 (this will be the key), names, brief descriptions, and output this data as a JS object called unitData.

And the data returned was below:

  • unit-data.js
const unitData = {
  unit_01: {
    name: "Fashion Boutique",
    description:
      "Discover the latest in fashion. From casual to formal attire, we've got you!",
    opening_hours: '9am - 6pm'
  },
  unit_02: {
    name: "Electronics Hub",
    description:
      "Your one-stop shop for the latest electronics, gadgets, and accessories.",
    opening_hours: '9am - 6pm'
  },
  unit_03: {
    name: "Café Delight",
    description:
      "Relax and enjoy our selection of coffee, tea, and pastries in a cozy atmosphere.",
    opening_hours: '9am - 6pm'
  },
  unit_04: {
    name: "Book Haven",
    description:
      "A paradise for book lovers. Find the latest bestsellers and timeless classics.",
    opening_hours: '9am - 6pm'
  },
  unit_05: {
    name: "Tech World",
    description:
      "Explore cutting-edge technology and innovative gadgets.",
    opening_hours: '9am - 6pm'
  },
  unit_06: {
    name: "Gourmet Grocer",
    description:
      "Fresh produce, artisanal breads, and organic products.",
    opening_hours: '9am - 6pm'
  },
  unit_07: {
    name: "Beauty Essentials",
    description:
      "Your destination for skincare, makeup, and beauty products.",
    opening_hours: '9am - 6pm'
  },
  unit_08: {
    name: "Toy Land",
    description:
      "A magical place filled with toys and games for kids of all ages.",
    opening_hours: '9am - 6pm'
  },
  unit_09: {
    name: "Sports Arena",
    description:
      "Everything you need for sports, fitness, and outdoor activities.",
    opening_hours: '9am - 6pm'
  },
  unit_10: {
    name: "Jewelry Box",
    description:
      "Elegant and timeless pieces to adorn every occasion.",
    opening_hours: '9am - 6pm'
  },
  unit_11: {
    name: "Home Decor",
    description:
      "Stylish furniture and decor to make your house a home.",
    opening_hours: '9am - 6pm'
  },
  unit_12: {
    name: "Pet Paradise",
    description:
      "All you need for your furry friends, from food to accessories.",
    opening_hours: '9am - 6pm'
  },
  unit_13: {
    name: "Music Store",
    description:
      "Instruments, records, and all things music.",
    opening_hours: '9am - 6pm'
  },
  unit_14: {
    name: "Cinema House",
    description:
      "Latest movies and a comfortable viewing experience.",
    opening_hours: '9am - 6pm'
  },
  unit_15: {
    name: "Art Gallery",
    description:
      "Exquisite artworks from local and international artists.",
    opening_hours: '9am - 6pm'
  },
  unit_16: {
    name: "Gift Shop",
    description:
      "Unique gifts for every special occasion.",
    opening_hours: '9am - 6pm'
  },
  unit_17: {
    name: "Travel Agency",
    description:
      "Plan your next adventure with our expert guides.",
    opening_hours: '9am - 6pm'
  },
  unit_18: {
    name: "Pharmacy Plus",
    description:
      "Your health is our priority. Medicines and healthcare products.",
    opening_hours: '9am - 6pm'
  },
  unit_19: {
    name: "Shoe Corner",
    description:
      "Step into style with our wide range of footwear.",
    opening_hours: '9am - 6pm'
  },
  unit_20: {
    name: "Optical Center",
    description:
      "Vision care and fashionable eyewear.",
    opening_hours: '9am - 6pm'
  },
  unit_21: {
    name: "Kids' Fashion",
    description:
      "Trendy and comfortable clothing for children.",
    opening_hours: '9am - 6pm'
  },
  unit_22: {
    name: "Sweet Treats",
    description:
      "Indulge in our selection of candies and chocolates.",
    opening_hours: '9am - 6pm'
  },
  unit_23: {
    name: "Bakery Fresh",
    description:
      "Freshly baked bread, pastries, and cakes.",
    opening_hours: '9am - 6pm'
  },
  unit_24: {
    name: "Stationery World",
    description:
      "All your office and school supplies under one roof.",
    opening_hours: '9am - 6pm'
  },
  unit_25: {
    name: "Flower Shop",
    description:
      "Fresh flowers and arrangements for every occasion.",
    opening_hours: '9am - 6pm'
  },
  unit_26: {
    name: "Mobile Store",
    description:
      "Latest smartphones and mobile accessories.",
    opening_hours: '9am - 6pm'
  },
  unit_27: {
    name: "Ice Cream Parlor",
    description:
      "Cool off with our delicious ice cream flavors.",
    opening_hours: '9am - 6pm'
  },
  unit_28: {
    name: "Watch Emporium",
    description:
      "Timepieces that combine style and function.",
    opening_hours: '9am - 6pm'
  },
  unit_29: {
    name: "Suit Up",
    description:
      "Tailored suits and formal wear for men.",
    opening_hours: '9am - 6pm'
  },
  unit_30: {
    name: "Women's Wear",
    description:
      "Fashionable clothing and accessories for women.",
    opening_hours: '9am - 6pm'
  },
  unit_31: {
    name: "Men's Den",
    description:
      "Casual and formal wear for men.",
    opening_hours: '9am - 6pm'
  },
  unit_32: {
    name: "Luggage Land",
    description:
      "Travel in style with our range of luggage.",
    opening_hours: '9am - 6pm'
  },
  unit_33: {
    name: "Digital Photography",
    description:
      "Cameras, lenses, and photography equipment.",
    opening_hours: '9am - 6pm'
  },
  unit_34: {
    name: "Fitness Center",
    description:
      "Gym equipment and fitness accessories.",
    opening_hours: '9am - 6pm'
  },
  unit_35: {
    name: "DIY Hardware",
    description:
      "Tools and materials for all your DIY projects.",
    opening_hours: '9am - 6pm'
  },
  unit_36: {
    name: "Garden Center",
    description:
      "Plants, seeds, and gardening supplies.",
    opening_hours: '9am - 6pm'
  },
  unit_37: {
    name: "Cosmetics Corner",
    description:
      "Latest trends in cosmetics and beauty products.",
    opening_hours: '9am - 6pm'
  },
  unit_38: {
    name: "Antique Shop",
    description:
      "Vintage items and collectibles from around the world.",
    opening_hours: '9am - 6pm'
  },
  unit_39: {
    name: "Wine Cellar",
    description:
      "A selection of fine wines and spirits.",
    opening_hours: '9am - 6pm'
  },
  unit_40: {
    name: "Bookstore Café",
    description:
      "Enjoy a good book with a cup of coffee.",
    opening_hours: '9am - 6pm'
  },
  unit_41: {
    name: "Tea House",
    description:
      "A variety of teas and tea accessories.",
    opening_hours: '9am - 6pm'
  },
  unit_42: {
    name: "Outdoor Gear",
    description:
      "Equipment for camping, hiking, and adventure sports.",
    opening_hours: '9am - 6pm'
  },
  unit_43: {
    name: "Bag Boutique",
    description:
      "Handbags, backpacks, and wallets.",
    opening_hours: '9am - 6pm'
  },
  unit_44: {
    name: "Hobby Shop",
    description:
      "Models, crafts, and supplies for hobbyists.",
    opening_hours: '9am - 6pm'
  },
  unit_45: {
    name: "Electronics Repair",
    description:
      "We fix your gadgets quickly and efficiently.",
    opening_hours: '9am - 6pm'
  },
  unit_46: {
    name: "Dance Studio",
    description:
      "Learn various dance forms with our expert instructors.",
    opening_hours: '9am - 6pm'
  },
  unit_47: {
    name: "Music Lessons",
    description:
      "Private and group lessons for various instruments.",
    opening_hours: '9am - 6pm'
  },
  unit_48: {
    name: "Language Center",
    description:
      "Language courses and learning materials.",
    opening_hours: '9am - 6pm'
  },
  unit_49: {
    name: "Travel Gear",
    description:
      "All the essentials for your next trip.",
    opening_hours: '9am - 6pm'
  },
  unit_50: {
    name: "Gaming Zone",
    description:
      "Latest video games and consoles.",
    opening_hours: '9am - 6pm'
  },
  unit_51: {
    name: "Health Food",
    description:
      "Organic and health-focused food products.",
    opening_hours: '9am - 6pm'
  },
  unit_52: {
    name: "Salon & Spa",
    description:
      "Relax and rejuvenate with our spa treatments.",
    opening_hours: '9am - 6pm'
  },
  unit_53: {
    name: "Photo Studio",
    description:
      "Professional photography services.",
    opening_hours: '9am - 6pm'
  },
  unit_54: {
    name: "Tailor Shop",
    description:
      "Custom tailoring and alterations.",
    opening_hours: '9am - 6pm'
  },
};

Now, we have the names/store information for all of our stores in one unit-data.js.

7. Opening the Pop-Up on Click

Now, we are just about ready to starting making things happen – allowing users to click and interact with the store units!

7.1 Setting Up Our Main.js File

Let’s declare our variables first.

  • main.js
document.addEventListener("DOMContentLoaded", () => {

    const units = document.querySelectorAll('.unit');
    const unitPopup = document.getElementById('unit-popup');
    const unitPopupContent = document.getElementById('unit-content');
    const unitPopupClose = document.getElementById('unit-popup-close');
});

Notes:

  • Above, I’m going to wait until the DOM has fully loaded, and then:
  • Declare the script’s four main variables:
    • units: Each SVG shape (rectangle, polygon or circle) unit.
    • unitPopup: The modal (pop-up).
    • unitPopupContent: The inner content area that we will be using to dynamically insert the store’s information.
    • unitPopupClose: The button (X) to close the pop-up.

7.2 Loop Through Units and Show Pop-Up Box on Click

I think now is the time for the most important (and fun) part of this interactive shopping mall map tutorial – clicking on the stores and opening up the pop-up box to display the store name and information 🤩

After our declared variables, I’m going to loop through each unit, add a click event listener, pull in each of the store’s data, and pass this to another function showPopup() – that we will create momentarily.

  • main.js
...

/** 
 * Open unit pop-up box on click
 */
units.forEach(unit => {
   if (!unit.classList.contains('unit-unavailable')) {
      unit.addEventListener('click', (event) => {
         event.stopPropagation(); 
         const unitId = event.target.id;
         const data = unitData[unitId];
         if (data) {
            showPopup(event, data);
         }
      });
   }
});

Don’t worry that this doesn’t work just yet. We’ll create the showPopup() function in the next section to make the magic happen.

Notes

In the above code, we have basically done the following:

  • Looped through all of our units in the floor plan (making sure to exclude unavailable units).
  • Added a click event handler to each unit
  • Stops the click event bubbling up through the document. It basically stops parent elements’ event handlers from firing after the current event handler runs.
  • const unitId = event.target.id; This retrieves the id attribute from the specific element on which the event occurred (unit_31 for example).
  • const data = unitData[unitId]; This looks up the corresponding store information from the unitData object using the unitId as the key.
  • And finally, we run the showPopup(event, data); function, passing in two parameters for the store info, and the event data, for later on.

7.3 Passing in the Unit Data to the Pop-Up Function

As stated in the previous section, we’ve passed in two objects to the showPopup function – event and data. Below is what both objects contain, when I output them with console.log.

We’ll pull in the data first, and in the next section we’ll play with the event object.

data object

event object

  • main.js
/** 
 * Open unit pop-up box on click
 */

...

function showPopup(event, data) {
   unitPopupContent.innerHTML = `
      <h3>${data.name}</h3>
      <p id="unit-description">${data.description}</p>
      <p id="unit-hours"><strong>Opening Hours:</strong>. 
      <br>
      ${data.opening_hours}</p>
   `;

   unitPopup.style.display = 'block';
}

The above should be pretty self explanatory, but all we’re doing is pulling in the store details of the unit that was clicked, and then inserting it all into the #unitPopupContent div.

So, when we click on any random unit, we get a pop-up!

7.4 Opening the Pop-up Close to the Clicked Store

It’s great that our little modal pop-up box opens up, but you’ll notice that it opens up to the left hand side of the screen, regardless of whatever unit we click.

Let’s change this, so that the pop-up box opens up close to the unit that was clicked. We will need to refactor our showPopup() function.

  • main.js
function showPopup(event, data) {
   unitPopupContent.innerHTML = `
      <h3>${data.name}</h3>
      <p id="unit-description">${data.description}</p>
      <p id="unit-hours"><strong>Opening Hours:</strong>. <br>
      ${data.opening_hours}</p>
   `;

   // Get mouse position relative to the document
   const mouseX = event.pageX;
   const mouseY = event.pageY;

   // Set the position of the popup
   unitPopup.style.left = mouseX + 'px';
   unitPopup.style.top = mouseY + 'px';
   unitPopup.style.display = 'block';

}

Above the unitPopup.style.display = 'block'; code block – you can see that we’ve captured the mouse coordinates of the unit that was clicked. We then set these captured X and Y coordinates for the pop-up to open underneath the area that was clicked.

And we get the following:

7.5 Pop-up Fix

We have a pretty nice interactive shopping mall map now, but I spotted one issue – if I click on a unit near to the right hand side of the viewport, the pop-up box goes out of view 👎🏻

Not a problem though – we can just check if the pop-up modal goes out of frame in the window viewport!

  • main.js
function showPopup(event, data) {
   
   ...

   // Adjust position if the pop-up is going off-screen
   const popupRect = unitPopup.getBoundingClientRect(); 

   if (popupRect.right >= window.innerWidth) {
      unitPopup.style.right = 50 + 'px';
      unitPopup.style.left = 'auto';
   }
   if (popupRect.bottom >= window.innerHeight) {
      unitPopup.style.top = 'auto'
      unitPopup.style.bottom = 50 + 'px';
   }
}

Notes

Above, we have used the getBoundingClientRect() method to give us some data about the pop-up modal – more specifically, about the right and bottom values of this div.

We can then check if these values overlap the browser viewport, if they do, remove the left property value and give the pop-up a right value, a bottom value or both of 50px.

And now we have squashed this little bug!

8. Closing the Modal Pop-up box

Everything is basically done with our little interactive map, bar one last important piece of functionality… we need to allow the pop-up box to be closed!

  • main.js
function showPopup(event, data) {
   
   ...

   // Hide unit pop-up when clicking anywhere outside of the unit pop-up
   document.addEventListener('click', () => {
      unitPopup.style.display = 'none';
   });

   // Close unit pop-up box on hitting the escape key
   document.addEventListener('keydown', (event) => {
      if (event.key === 'Escape' || event.key === 'Esc') {
         unitPopup.style.display = 'none';
      }
   });

   // Close unit pop-up box on 'close' button click 
   unitPopupClose.addEventListener('click', () => {
      unitPopup.style.display = 'none';
   });

   // Prevent clicks inside the unit pop-up from closing it
   unitPopup.addEventListener('click', (event) => {
      event.stopPropagation(); 
   });

}

Everything should be pretty easy to understand here, the only thing I will touch on is the final event click listener. This event.stopPropagation(); function ensures that the click on the pop-up box only fires on the pop-up itself, and doesn’t “bubble up” through the DOM to other elements.

9. Conclusion

So, there we have it. We have made a nice little interactive shopping mall map, allowing users to quickly get information about a unit. You can use this, and expand upon it to create some complex maps if you wish, maybe a second level etc.

If this tutorial has helped you, I wouldn't say no to a coffee as a tip ☕️

Buy Me a Coffee at ko-fi.com

Leave a Reply

Your email address will not be published. Required fields are marked *