0

I am trying to use unobtrusive-ajax to allow a site to update its contents as AJAX if JavaScript is available and as static pages if not. I'd like to support the browser's Back button moving backwards in history.

I am using JavaScript's history API to manipulate the browser history as a user navigates the website. I am storing both the HTML (through innerHTML) and the current state of the DOM (through JQuery's serialize method) in the history object. When the user hits Back, the HTML and then DOM are restored from the cached HTML and serialized DOM, respectively.

However, I'm losing the information about checkboxes that were selected when the page loaded ("checked"="checked") but that the user unselected.

Per the JQuery docs at https://api.jquery.com/serialize/

Values from checkboxes and radio buttons (inputs of type "radio" or "checkbox") are included only if they are checked.

"Values" here refers to the checked state, not the value of the checkbox.

Is this a mis-design? Shouldn't it include the checked value when it differs from the HTML?

Are there other properties on other elements that are conditionally serialized?

  • The state of the checkbox is not saved anyplace when the User moves Forward? – Twisty Feb 17 '21 at 22:51
  • @Twisty The state of the checkbox is saved server-side, but that's not available to the browser on back. It is only saved client-side if it is checked (work-around to uncheck all the boxes before DOM restore, but that seems hacky). – MichaelMitchell Feb 17 '21 at 23:04

2 Answers2

0

Whatever the browser history manipulation history you are attempting... The main deal here is to save the checkboxes state in real time so every time JS runs, you retreive the saved values.

Saving the state of checkboxes can be done via localStorage.

What is below works perfectly on page reload. Your history manipulations should walk around the fact the "normal" behavior of the back button NOT to run again the JS. I leave it to you. ;)

// Check box state array
let checkboxesState = []

// If that is the very first page load and there is no local storage.
if(localStorage.getItem("checkedCheckboxes") === null){

  $("input[type='checkbox']").each(function(index){
    checkboxesState.push(this.checked)

    // Add a data attribute for the index of the checkbox
    this.setAttribute("data-chk_index",index)
  })
  // Save
  localStorage.setItem("checkedCheckboxes", JSON.stringify(checkboxesState))
}

// If there already is a checkbox state storage
else{
  checkboxesState = JSON.parse(checkboxesState)
  
  $("input[type='checkbox']").each(function(index){
    this.checked = checkboxesState[index]

    // Add a data attribute for the index of the checkbox
    this.setAttribute("data-chk_index",index)
  })
}

// Update the array on click
$("input[type='checkbox']").on("click", function(){
  checkboxesState[this.getAttribute("data-chk_index")] =  this.checked
  
  // Update Storage
  localStorage.setItem("checkedCheckboxes", JSON.stringify(checkboxesState))
  
  // That is the current state of the saved array
  console.log(localStorage.getItem("checkedCheckboxes"))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="checkbox">
<input type="checkbox" checked>
<input type="checkbox">

Check my CodePen since localStorage is not allowed in SO snippets.

Louys Patrice Bessette
  • 33,375
  • 6
  • 36
  • 64
0

JQuery's serialize is meant for serializing forms for submission to the server, which would assume checkboxes aren't checked unless stated otherwise.

The issue was that deserialize from https://github.com/kflorence/jquery-deserialize/ is not blanking out the properties before applying the ones in the serialized string.

I worked around the issue by unchecking all the checkboxes before applying the deserialization.

document.getElementById("thepage").innerHTML = stateHtml;
$("#thepage input[type='checkbox']").prop("checked", false);
$("#thepage").deserialize(stateDomSerialized);