css 如何使用localStorage进行“标记为已读”?

isr3a4wc  于 2022-12-01  发布在  其他
关注(0)|答案(3)|浏览(242)

我正在尝试弄清楚如何在刷新页面后保持“Mark as Read”为“Mark as Unread”。反之亦然。我如何使用localStorage保存数据?到目前为止,这是我的“Mark as Read”代码:
第一个
我单击“标记为已读”,它变成了“标记为未读”。但刷新页面后,它又回到了“标记为已读”。如何避免这种情况?

sulc1iza

sulc1iza1#

在脚本中,您需要更改两项内容:

<script>
  function readunread() {
    currentvalue = document.getElementById("readunread").value;
    if (currentvalue == "Mark as Unread") {
      document.getElementById("readunread").value = "Mark as Read";
      // 1. Update the localstorage
      localStorage.setItem("readunread", "Mark as Read");
    } else {
      document.getElementById("readunread").value = "Mark as Unread";
      // 1. Update the localstorage
      localStorage.setItem("readunread", "Mark as Unread");
    }
  }
</script>

<input
  type="button"
  value="Mark as Read"
  id="readunread"
  onclick="readunread();"
/>

<script>
  // 2. Get the value from the local storage
  function loadInitialValue() {
    const localValue = localStorage.getItem("readunread");

    console.log(localValue);
    if (localValue == "Mark as Unread") {
      document.getElementById("readunread").value = "Mark as Unread";
    } else {
      document.getElementById("readunread").value = "Mark as Read";
    }
  }

  loadInitialValue(); // Make sure to call the function
</script>
uqdfh47h

uqdfh47h2#

为了使用localStorage作为持久性数据存储来管理项的已读/未读状态,您需要序列化(un)读取状态作为某种字符串以作为值存储在存储区中(因为localStorage只存储字符串值),然后在检索该值时对其进行反序列化。JSON是序列化格式的一个可访问选择,因为它自然地表示许多JavaScript数据结构,并且易于parse/stringify
像这样的问题总是很难在一个工作的代码片段中演示,因为Stack Overflow的代码片段环境是沙盒的,并且阻止访问localStorage之类的东西,所以当你试图使用这些特性时,由于缺乏权限,会抛出一个运行时异常。
下面我提供了一个自包含的例子,使用基本的functional programming技术来存储一个条目列表的已读/未读状态,以保持代码的组织性。你可以将代码复制粘贴到你电脑上的本地HTML文件中,然后使用本地静态文件web服务器提供(例如,DenoPython等)来查看它的工作情况。我已经为您提供了详细的注解,以解释程序的每一步都发生了什么。
如果要在测试演示时检查localStorage的状态,请参阅问题How to view or edit localStorage?

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />

  <title>LocalStorage: read/unread items</title>

  <style>
    /* Just some styles for this example: styling is up to you */

    * { box-sizing: border-box; }
    body { font-family: sans-serif; }

    .toggle-status {
      font-size: 1rem;
      padding: 0.25rem;
      width: 8rem;
    }

    #list {
      list-style: none;
      padding: 0;
      display: flex;
      flex-direction: column;
      gap: 0.5rem;
      align-items: flex-start;
    }

    .item {
      display: flex;
      gap: 1rem;
      align-items: center;
    }

    .item.read > .item-content { font-weight: normal; }
    .item.unread > .item-content { font-weight: bold; }
  </style>

  <script type="module">
    // Get the state of all of the read/unread items from localStorage
    // as an object:
    function getStatusMap () {
      try {
        // Get the JSON value from local storage:
        // if it doesn't exist, it will be null, so use a default value instead:
        // a JSON string representing an empty object:
        const storageValue = window.localStorage.getItem('read_status_map') ?? '{}';

        // Parse the string value into an actual object:
        const readStatusMap = JSON.parse(storageValue);

        // Return the value if it's a plain object:
        if (
          typeof readStatusMap === 'object'
          && readStatusMap !== null
          && !Array.isArray(readStatusMap)
        ) return readStatusMap;

        // Else throw an error because it was an invalid value:
        throw new Error('Unepxected value');
      }
      catch (ex) {
        // Catch any exception which might have occurred.
        // You can handle it however you want (or just ignore it).
        // For example, you could print it
        // to the console error stream to view it:
        console.error(ex);
        
        // Return an empty object as the default:
        return {};
      }
    }

    // Update the localStorage state of all the read/unread items:
    function setStatusMap (statusMap) {
      const json = JSON.stringify(statusMap);
      const storageValue = window.localStorage.setItem('read_status_map', json);
    }

    // Update the read/unread status for a single item:
    function updateStatus (statusMap, listItemElement, isReadStatus) {
      const button = listItemElement.querySelector(':scope > button.toggle-status');

      // Depending on the current status, update the action button's text
      // to describe the next (opposite) action:
      button.textContent = `Mark as ${isReadStatus ? 'unread' : 'read'}`;

      // Get the ID from the list item's data attribute:
      const {id} = listItemElement.dataset;

      // Get the state object of the current item from the status map object,
      // OR create one if it doesn't exist yet. You can store other information
      // about each item here, but — in this example — only the ID (string)
      // and read status (boolean) properties are stored:
      const status = statusMap[id] ??= {id, isRead: false};

      // Update the read status of the item:
      status.isRead = isReadStatus;

      // Update the whole state in localStorage:
      setStatusMap(statusMap);

      // Optional: update the list item's read/unread class.
      // This can help with applying CSS styles to the items:
      if (isReadStatus) {
        listItemElement.classList.add('read');
        listItemElement.classList.remove('unread');
      }
      else {
        listItemElement.classList.remove('read');
        listItemElement.classList.add('unread');
      }
    }

    // A convenience function which toggles between read/unread for an item:
    function toggleStatus (statusMap, listItemElement) {
      // Get the ID from the list item's data attribute:
      const {id} = listItemElement.dataset;

      // Get the current status (or false by default if it doesn't exist yet):
      let isRead = statusMap[id]?.isRead ?? false;

      // Toggle it to the opposite state:
      isRead = !isRead;

      // Update it:
      updateStatus(statusMap, listItemElement, isRead);
    }

    // Now, using the functions above together:
    function main () {
      // Get the initial read/unread status map:
      const statusMap = getStatusMap();

      // Get an array of the item elements:
      const listItemElements = [...document.querySelectorAll('#list > li.item')];

      for (const listItemElement of listItemElements) {
        // Get the ID from the list item's data attribute:
        const {id} = listItemElement.dataset;

        // Set the initial read status for each item to what was found
        // in localStorage, or if nothing was found then set to false by default:
        const initialStatus = statusMap[id]?.isRead ?? false;
        updateStatus(statusMap, listItemElement, initialStatus);

        const button = listItemElement.querySelector(':scope > button.toggle-status');

        // Set an action for each item's toggle button: when it is clicked,
        // toggle the status for that item. Formally, this is called "binding an
        // event listener callback to the button's click event":
        button.addEventListener(
          'click',
          () => toggleStatus(statusMap, listItemElement),
        );
      }
    }

    // Invoke the main function:
    main()
  </script>
</head>

<body>
  <!--
    A list of items, each with:
    - a unique ID
    - a toggle button,
    - and some text content
  -->
  <ul id="list">
    <li class="item" data-id="cc9e88ce-3ed4-443a-84fc-fa7147baa025">
      <button class="toggle-status">Mark as read</button>
      <div class="item-content">First item content</div>
    </li>
    <li class="item" data-id="23a9204c-905f-48db-9f6a-deb3c8f82916">
      <button class="toggle-status">Mark as read</button>
      <div class="item-content">Second item content</div>
    </li>
    <li class="item" data-id="18b47e4c-635f-49c0-924e-b9088538d08a">
      <button class="toggle-status">Mark as read</button>
      <div class="item-content">Third item content</div>
    </li>
    <li class="item" data-id="ed2aacca-64f0-409d-8c1b-d1bdcb7c6058">
      <button class="toggle-status">Mark as read</button>
      <div class="item-content">Fourth item content</div>
    </li>
    <li class="item" data-id="0fce307b-656a-4102-9dc9-5e5be17b068d">
      <button class="toggle-status">Mark as read</button>
      <div class="item-content">Fifth item content</div>
    </li>
    <!-- ...etc. -->
  </ul>
</body>

</html>
fnvucqvd

fnvucqvd3#

你已经很接近了。剩下的就是使用本地存储了。为此,用下面的代码替换你的JavaScript:

// On Load
const readUnreadButton = document.getElementById("readunread");
document.getElementById("readunread").value =
  localStorage.getItem("readunread") || readUnreadButton.value;

// On Click
function readunread() {
  const readUnreadButton = document.getElementById("readunread");
  currentvalue = readUnreadButton.value;
  if (currentvalue == "Mark as Unread") {
    readUnreadButton.value = "Mark as Read";
    localStorage.setItem("readunread", "Mark as Read");
  } else {
    readUnreadButton.value = "Mark as Unread";
    localStorage.setItem("readunread", "Mark as Unread");
  }
}

相关问题