The following is an example of a JSON file that contains a key value pair with a URL under "next_page_url": "https://www.example.com/example.json",
which indicates the next JSON file in my XMLHttpRequest GET request. The GET request could contain hundreds of pages.
{
"count": 135,
"description": "Documents published from 01/01/2020 to 01/01/2050 and from Surface Mining Reclamation and Enforcement Office",
"total_pages": 7,
"next_page_url": "https://www.example.com/example.json",
"results": [
{
"title": "Agency Information Collection Activities; Revisions; Renewal; and Transfer, Assignment, or Sale of Permit Rights",
"type": "Notice",
"abstract": "In accordance with the Paperwork Reduction Act of 1995, we, the Office of Surface Mining Reclamation and Enforcement (OSMRE), are proposing to renew an information collection.",
"document_number": "2023-09869",
"html_url": "https://www.federalregister.gov/documents/2023/05/09/2023-09869/agency-information-collection-activities-revisions-renewal-and-transfer-assignment-or-sale-of-permit",
"pdf_url": "https://www.govinfo.gov/content/pkg/FR-2023-05-09/pdf/2023-09869.pdf",
"public_inspection_pdf_url": "https://public-inspection.federalregister.gov/2023-09869.pdf?1683549934",
"publication_date": "2023-05-09",
"agencies": [
{
"raw_name": "DEPARTMENT OF THE INTERIOR",
"name": "Interior Department",
"id": 253,
"url": "https://www.federalregister.gov/agencies/interior-department",
"json_url": "https://www.federalregister.gov/api/v1/agencies/253",
"parent_id": null,
"slug": "interior-department"
},
{
"raw_name": "Office of Surface Mining Reclamation and Enforcement",
"name": "Surface Mining Reclamation and Enforcement Office",
"id": 480,
"url": "https://www.federalregister.gov/agencies/surface-mining-reclamation-and-enforcement-office",
"json_url": "https://www.federalregister.gov/api/v1/agencies/480",
"parent_id": 253,
"slug": "surface-mining-reclamation-and-enforcement-office"
}
]
]
}
I have the following script, html, and CSS to request and display the JSON data as desired.
function loadBLM() {
const xmlhttp = new XMLHttpRequest();
xmlhttp.onload = function () {
const myArr = JSON.parse(this.responseText);
let text = "<button type='button' class='btn btn-primary' data-toggle='collapse' data-target='#collapse'>Info</button>";
for (let i in myArr.results) {
let agencies = myArr.results[i].agencies.map((a) => a.raw_name).join(" - ");
text +=
"<div class='fed-reg-container'><h2 class='title'>" +
myArr.results[i].title +
"</h2><p>" +
myArr.results[i].type +
"</p><p>" +
agencies +
"</p><p>Document # " +
myArr.results[i].document_number +
"</p><p>Posted on: " +
myArr.results[i].publication_date +
"</p><p>" +
myArr.results[i].abstract +
"</p><a class='fed-reg-button' href='" +
myArr.results[i].html_url +
"'>Read More</a></div>";
}
text += "<div class='pages'><button class='btn btn-info' onclick='loadNextPage()'>Next Page</button></div>";
document.getElementById("demo").innerHTML = text;
document.getElementById("data-info").innerHTML = myArr.count + " " + myArr.description;
};
xmlhttp.open(
"GET",
"https://www.federalregister.gov/api/v1/documents.json?conditions%5Bagencies%5D%5B%5D=land-management-bureau&conditions%5Bpublication_date%5D%5Byear%5D=2023",
true
);
xmlhttp.send();
}
.fed-reg-container {
background-color: black;
color: white;
padding: 20px;
margin: 20px 0;
}
.title {
color: #fcb900;
}
.fed-reg-button {
background-color: #fcb900;
color: black;
padding: 10px;
display: block;
max-width: 100px;
text-align: center;
font-weight: 600;
text-decoration: none;
}
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
</head>
<button class="btn btn-info" type="button" onclick="loadBLM()">Bureau of Land Management</button>
<br><br>
<div class="container">
<div id="collapse" class="collapse">
<h2>About this resource</h2>
<p id='data-info'></p>
</div>
</div>
<div id="demo"></div>
How can I send another XMLHttpRequest using the URL returned in the "next_page_url": "https://www.example.com/example.json",
when the <button class='btn btn-info' onclick='loadNextPage()'>Next Page</button>
is clicked? It should repeat the original request using the URL in "nextpage_url"
.
I have tried adding another function to make another XMLHttpRequest and it does not work. Would a nested function be the proper way?
Thank you!
You can update your loadBLM
function to accept a URL to which you want to make the request. Your Next page button can then call the loadBLM()
function again, but pass the nextpage_url
into it. I suggest also checking if the nextpage_url
property exists on the response object before displaying the next page button, for example:
if (myObj.next_page_url)
text += `<div class='pages'><button class='btn btn-info' onclick='loadBLM("${myObj.next_page_url}")'>Next Page</button></div>`;
I also suggest that you fix your variable names. myArr
isn't an array, it's an object, myArr.results
is an array. In the snippet below I've updated the variable names. In addition, for...in
loops shouldn't be used to loop over arrays, instead, if you want to loop over an array's elements, use a regular for
loop, a for..of
loop, or a .forEach()
loop.
See the snippet below for full code:
function loadBLM(endpoint = "https://www.federalregister.gov/api/v1/documents.json?conditions%5Bagencies%5D%5B%5D=land-management-bureau&conditions%5Bpublication_date%5D%5Byear%5D=2023") {
const xmlhttp = new XMLHttpRequest();
xmlhttp.onload = function() {
const myObj = JSON.parse(this.responseText);
const myArr = myObj.results;
let text = "<button type='button' class='btn btn-primary' data-toggle='collapse' data-target='#collapse'>Info</button>";
for (const result of myArr) {
let agencies = result.agencies.map((a) => a.raw_name).join(" - ");
text +=
"<div class='fed-reg-container'><h2 class='title'>" +
result.title +
"</h2><p>" +
result.type +
"</p><p>" +
agencies +
"</p><p>Document # " +
result.document_number +
"</p><p>Posted on: " +
result.publication_date +
"</p><p>" +
result.abstract +
"</p><a class='fed-reg-button' href='" +
result.html_url +
"'>Read More</a></div>";
}
if (myObj.next_page_url)
text += `<div class='pages'><button class='btn btn-info' onclick='loadBLM("${myObj.next_page_url}")'>Next Page</button></div>`;
document.getElementById("demo").innerHTML = text;
document.getElementById("data-info").innerHTML = myObj.count + " " + myObj.description;
};
xmlhttp.open(
"GET",
endpoint,
true
);
xmlhttp.send();
}
.fed-reg-container {
background-color: black;
color: white;
padding: 20px;
margin: 20px 0;
}
.title {
color: #fcb900;
}
.fed-reg-button {
background-color: #fcb900;
color: black;
padding: 10px;
display: block;
max-width: 100px;
text-align: center;
font-weight: 600;
text-decoration: none;
}
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
</head>
<button class="btn btn-info" type="button" onclick="loadBLM()">Bureau of Land Management</button>
<br><br>
<div class="container">
<div id="collapse" class="collapse">
<h2>About this resource</h2>
<p id='data-info'></p>
</div>
</div>
<div id="demo"></div>
I would also note that these days, using XMLHttpRequest
is quite outdated. Browsers (and other environments like NodeJS) provide a new API for fetching data called fetch()
. In addition, it's not best practice to use inline event handler attributes in your HTML like onclick
, instead, it's preferred to use methods like .addEventListener()
. I would also suggest refactoring your code to pull out your HTML into reusable functions, for example:
async function loadJSON(url) {
const response = await fetch(url);
if (!response.ok) // check if response worked (no 404 errors etc...)
throw new Error(response.statusText);
const data = response.json(); // get JSON from the response
return data;
}
function createCard(data) {
const agencies = data.agencies.map((a) => a.raw_name).join(" - ");
return `<div class='fed-reg-container'>
<h2 class='title'>${data.title}</h2>
<p>${data.type}</p>
<p>${agencies}</p>
<p>Document # ${data.document_number}</p>
<p>Posted on: ${data.publication_date}</p>
<p>${data.abstract}</p>
<a class='fed-reg-button' href='${data.html_url}'>Read More</a>
</div>`;
}
function createInfoButton() {
return `<button type='button' class='btn btn-primary' data-toggle='collapse' data-target='#collapse'>Info</button>`;
}
function createControls(data) { // you could update this to include a prev page control
if (data.next_page_url)
return `<div class='pages'>
<button id='next-btn' class='btn btn-info'>Next Page</button>
</div>`;
return "";
}
function render(response) {
const myArr = response.results;
let text = createInfoButton();
for (const result of myArr) {
text += createCard(result);
}
text += createControls(response);
document.getElementById("demo").innerHTML = text;
document.getElementById("data-info").innerHTML = response.count + " " + response.description;
}
function createBLMLoader(endpoint) {
return async function loadBLM() {
const response = await loadJSON(endpoint);
endpoint = response.next_page_url;
render(response);
};
}
const API_URL = "https://www.federalregister.gov/api/v1/documents.json?conditions%5Bagencies%5D%5B%5D=land-management-bureau&conditions%5Bpublication_date%5D%5Byear%5D=2023";
const loadBLM = createBLMLoader(API_URL);
const blmBtn = document.getElementById("blm-btn");
const demoContainer = document.getElementById("demo");
blmBtn.addEventListener("click", loadBLM);
demoContainer.addEventListener("click", (e) => { // event-delegation
if(e.target?.matches("#next-btn")) { // if the next-btn was clicked
loadBLM();
}
});
.fed-reg-container {
background-color: black;
color: white;
padding: 20px;
margin: 20px 0;
}
.title {
color: #fcb900;
}
.fed-reg-button {
background-color: #fcb900;
color: black;
padding: 10px;
display: block;
max-width: 100px;
text-align: center;
font-weight: 600;
text-decoration: none;
}
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
</head>
<button id="blm-btn" class="btn btn-info" type="button">Bureau of Land Management</button>
<br><br>
<div class="container">
<div id="collapse" class="collapse">
<h2>About this resource</h2>
<p id='data-info'></p>
</div>
</div>
<div id="demo"></div>