htmlsvgscriptingmarkupcustom-data-attribute

Is it possible to change the text content displayed in an SVG image by setting specific data-* attributes in the html <svg> tag?


For example, to render the variously changing datetimes of a yearly-repeating event that lasts several days, having a re-usable single svg image that can take a data-datetime="yyyy-mm-dd" attribute from its svg tag and render that into a proper date that is subsequently displayed within the image. The reason one might want this is for say, a ticketing system that the display/splash/product image would otherwise have to be a separate image file for every day of the event, if you wanted that image to show the date within itself.


Solution

  • Something like this?

    const formattedDate = dateText => new Date(dateText).toLocaleDateString('en-US', {
      year: 'numeric',
      month: 'long',
      day: 'numeric'
    });
    
    const updateSVGDate = svgElement => {
      const dateText = svgElement.dataset.datetime;
      const dateEmoji = svgElement.dataset.emoji;
      if (dateText) {
        const textElement = svgElement.querySelector('.event-date');
        if (textElement) {
          textElement.textContent = formattedDate(`${dateText}T15:00:00`); // force date to not be midnight
          if (dateEmoji) textElement.textContent += ` ${dateEmoji}`;
        }
      }
    }
    window.addEventListener('load', () => { // when the page loads
      document.querySelectorAll('.event-svg').forEach(updateSVGDate)
    });
    <svg class="event-svg" data-datetime="2025-02-14" data-emoji="❤️" width="200" height="100">
        <rect width="200" height="100" fill="pink" />
        <text class="event-date" x="50%"
        y="50%"
        text-anchor="middle"
        dominant-baseline="middle" font-size="16" fill="black">Default Date</text>
    </svg>
    
    <svg class="event-svg" data-datetime="2025-04-01" data-emoji="🤡" width="200" height="100">
        <rect width="200" height="100" fill="lightblue" />
        <text class="event-date" x="50%"
        y="50%"
        text-anchor="middle"
        dominant-baseline="middle" font-size="16" fill="black">Default Date</text>
    </svg>

    Here is a dynamically generated svg using an array for the events and a lookup table for the emoji

    const formattedDate = dateText => {
      const normalized = `${dateText}T15:00:00`; // make sure we do not have issues with timezones
      return new Date(normalized).toLocaleDateString('en-US', {
        year: 'numeric',
        month: 'long',
        day: 'numeric',
      });
    };
    
    const generateMonthDates = (year, month) => {
      const lastDay = new Date(year, month + 1, 0); // Last day of the month
      const totalDays = lastDay.getDate(); // Number of days in the month
      return Array.from({ length: totalDays }, (_, i) => {
        const day = i + 1; // Days start at 1
        return `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
      });
    };
    
    // Generate SVGs for a specific month and year
    const svgNS = 'http://www.w3.org/2000/svg';
    const renderMonthSVGs = (year, month) => {
      const svgGrid = document.getElementById('svg-grid');
      const monthDates = generateMonthDates(year, month);
    
      monthDates.forEach(date => {
        const monthDay = date.slice(5); // Extract MM-DD
        const emojiData = emojis[monthDay] || {
          "background-color": "lightblue",
          "emoji": ""
        };
    
        // Create the SVG element
        const svg = document.createElementNS(svgNS, "svg");
        svg.setAttribute("class", "event-svg");
        svg.setAttribute("data-datetime", date); // in case we need it later
        svg.setAttribute("width", "200");
        svg.setAttribute("height", "100");
    
        // Create the rect element (background)
        const rect = document.createElementNS(svgNS, "rect");
        rect.setAttribute("width", "100%");
        rect.setAttribute("height", "100%");
        rect.setAttribute("fill", emojiData["background-color"]);
        svg.appendChild(rect);
    
        // Create the text element (date + emoji)
        const text = document.createElementNS(svgNS, "text");
        text.setAttribute("class", "event-date");
        text.setAttribute("x", "50%");
        text.setAttribute("y", "50%");
        text.setAttribute("text-anchor", "middle");
        text.setAttribute("dominant-baseline", "middle");
        text.setAttribute("font-size", "16");
        text.setAttribute("fill", "black");
        text.textContent = formattedDate(date) + (emojiData.emoji ? ` ${emojiData.emoji}` : "");
        svg.appendChild(text);
    
        // Append the SVG to the grid
        svgGrid.appendChild(svg);
      });
    };
    
    // Render February 2025 SVGs
    renderMonthSVGs(2025, 1); // 1 = February
    .grid {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      /* Adjust columns as needed */
      gap: 10px;
    }
    
    svg {
      border: 1px solid #ccc;
    }
    <div id="svg-grid" class="grid"></div>
    <script>
    
    // testdata
    
    const events = ["2025-02-14", "2025-02-16"]; // Specific event dates
    const emojis = {
      "02-14": {
        "background-color": "pink",
        "emoji": "❤️"
      },
      "02-16": {
        "background-color": "orange",
        "emoji": "🤡"
      }
    };
    </script>