How should I make a string safe to use as an HTML id (using JS)?

Hello.

The item key in localStorage could be any string.

I need to:

  • Make it safe to use as an HTML element's id
  • Do that in a way that is reversible

This is sloppy, and probably not quite right:

    function getSafeHtmlId(fixme){
      return (fixme.replace(/ /g, "___SPACE___").replace(/'/g, "___SINGLE_QUOTE___").replace(/"/g, "___DOUBLE_QUOTE___").replace(/:/g, "___COLON___").replace(/\./g, "___DOT___").replace(/\#/g,"___HASH___")); 
    };

CONTEXT

I've got a build of Quest that writes the transcript to localStorage, and the key will be "questtranscript-" + game.gamename.

So, that could be anything, and I wish to be able to use it as an id for a row in an HTML table which displays the user's list of transcripts in their localStorage.

Here is the entire document:

<!DOCTYPE html>
<html>
  <head>
    <title>Quest Transcripts</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <style>
      body {
        font-family: Source Sans Pro, Calibri, Candara, Arial, sans-serif;
        color: #333333;
      }
      #transcript-table {
        margin: 0 auto;
      }
      #transcript-table-header {
        text-align: center;
      }
    </style>
  </head>
  <body onload="init()">
    <table id="transcript-table">
      <tbody id="transcript-tbody">
        <tr>
          <th colspan="3" id="transcript-table-header">You have no transcripts</th>
        </tr>
        <!-- PLACEHOLDER -->
      </tbody>
    </table>
  </body>
  <script>
    var wnd;
    var tName = "";
    var choices = {};
    function getSafeHtmlId(fixme){
      return (fixme.replace(/ /g, "___SPACE___").replace(/'/g, "___SINGLE_QUOTE___").replace(/"/g, "___DOUBLE_QUOTE___").replace(/:/g, "___COLON___").replace(/\./g, "___DOT___").replace(/\#/g,"___HASH___")); 
    };
    function reverseSafeHtmlId(unfixme){
      return(unfixme.replace(/___SPACE___/g, " ").replace(/___SINGLE_QUOTE___/g, "'").replace(/___DOUBLE_QUOTE___/g, "\"").replace(/___COLON___/g, ":").replace(/___DOT___/g, ".").replace(/___HASH___/g, "#"));
    };
    function openTranscript(tsn){
      var tscriptData = localStorage.getItem(reverseSafeHtmlId(tsn)).replace(/@@@NEW_LINE@@@/g,"<br/>") || "No transcript data found.";
      wnd = window.open("about:blank", "", "_blank");
      wnd.document.write(tscriptData);
      wnd.document.title = tsn.replace(/questtranscript-/,"") + " - Transcript";
    };
    function removeTscript(tscript){
      var result = window.confirm("Delete this transcript?");
      if (result){
        console.log(tscript);
        localStorage.removeItem(reverseSafeHtmlId(tscript));
        document.getElementById(tscript).style.display = "none";
        delete choices[tscript.replace(/questtranscript-/,"")];
      }
      if (Object.keys(choices).length < 1){
        document.getElementById("transcript-table-header").innerHTML = "You have no transcripts.";
      }
    };
    var tScriptTemplate = "<tr id=\"TRANSCRIPT_NAME\" class=\"transcript-entry-holder\"><td class=\"transcript-name\">TRANSCRIPT_DISPLAYED_NAME</td><td class=\"transcript-open-link-holder\"><a href=\"#\"  name=\"TRANSCRIPT_NAME\" onclick=\"openTranscript(this.name);\" class=\"transcript-open-link\">OPEN</a></td><td class=\"transcript-delete-link-holder\"><a href=\"#\"  name=\"TRANSCRIPT_NAME\" onclick=\"removeTscript(this.name);\" class=\"transcript-delete-link\">DELETE</a></td></tr>";
    function init(){
      if (!isLocalStorageAvailable()){
        document.getElementById("transcript-table-header").innerHTML = "The transcript feature is not available in this browser.";
        return;
      }
      for (var e in localStorage) {
        if (e.startsWith("questtranscript-")){
          var eName = e.replace(/questtranscript-/,"");
          var safeName = getSafeHtmlId(eName);
          choices[safeName] = tScriptTemplate.replace(/TRANSCRIPT_NAME/g, "questtranscript-" + safeName).replace(/TRANSCRIPT_DISPLAYED_NAME/g, safeName);
        }
      }
      if (Object.keys(choices).length > 0){
        document.getElementById("transcript-table-header").innerHTML = "Your Transcripts";
        for (var tname in choices){
          $("#transcript-tbody").append(choices[tname]);
        }
      }
    };
    
    /* https://stackoverflow.com/a/16427747 */
    function isLocalStorageAvailable(){
        var test = 'test';
        try {
            localStorage.setItem(test, test);
            localStorage.removeItem(test);
            return true;
        } catch(e) {
            return false;
        }
    }
  </script>
</html>

This topic is now closed. Topics are closed after 14 days of inactivity.

Support

Forums