Reputation: 1191
I'm trying to write a JavaScript based pagination for a gallery, which loads HTML via an onClick
-event from the server. The HTML is simply appended via innerHtml
. The only difficulty is that I need to populate an array with thumbnail URLs for previews. The data for this is provided via the same HTTP request as the HTML.
I tried to add it via a <script>
tag to the HTML, but these are not executed when inserted via innerHtml
(see here).
Constraints:
Possible Solutions
Due to my limited knowledge of JavaScript I only see 2 solutions, of which none seems right:
innerHTML
and the array data could be passed to a function, when the HTTP request returns.<script>
element and extract its contents via some murky JS tricks. But I'm sure I will get beaten up for this.Any suggestion is highly appreciated.
For whom it may concern some code:
function loadNextPage(uri) {
var xhr = new XMLHttpRequest();
xhr.open('GET', uri);
xhr.onload = function() {
if (xhr.status === 200) {
var loadNextButton = document.getElementById("load-next-button");
loadNextButton.parentNode.removeChild(loadNextButton);
var vbw = document.getElementById("vbw");
vbw.innerHTML = vbw.innerHTML + xhr.response;
} else {
conosole.log('Failed: ' + xhr.status);
}
};
xhr.send();
}
<div id="vbw">
<script type="application/javascript">
let thumbNailStore = {
concat: function() {
console.log('Concat has been ran');
},
};
function addThumbnailListeners() {
console.log('addThumbnailListeners has been ran');
}
thumbNailStore.concat(); // This produces a JSON which is appended to an array in the <head> of the page
addThumbnailListeners();
</script>
<button id="load-next-button" onclick="loadNextPage('10')">Load more</button>
</div>
And the template:
@standardtemplate.commons.gallery(videoList, LandingPage.empty()) // That produces the actual gallery HTML
@if(pagination.getNextPath.isPresent) {
<script type="application/javascript">
thumbNailStore.concat(@Html(TemplateHelper.thumbListToJson(videoList))); // This produces a JSON which is appended to an array in the <head> of the page
addThumbnailListeners();
</script>
<button id="load-next-button" onclick="loadNextPage('@pagination.getNextPath.get()')">Load more</button>
}
Thanks!
Upvotes: 0
Views: 157
Reputation: 8855
Well your solution it is almost right, I would replace vbw.innerHTML = vbw.innerHTML + xhr.response;
with the .appendChild()
method. But I also notice that you are adding multiples <script type="application/javascript">...</script>
. So I think if you press the button Load More 2 times you will end up with 3 <script>
tags in your code.
Well, first extract the inline script inlineScript
and append it to your current script. Then, create a new document using the DOMParser().parseFromString()
method. This will parse your xhr.response
then get the <button>
element, and finally append it to your <div>
.
Here is an example:
let matches, clickCount = 0;
const regexScript = /<script[\s\S]*?>([\s\S]*?)<\/script>/gi;
const regexButton = /<button[\s\S]*?>[\s\S]*?<\/button>/gi;
let nodeCounter = document.getElementById('counter');
function getResponse() {
// To simulate response from your server
return `<script type="application/javascript">let thumbNailStore={concat:function(){console.log('Concat for videolist ${clickCount} ')}};function addThumbnailListeners(){console.log('addThumbnailListeners called ${clickCount}')}thumbNailStore.concat(),addThumbnailListeners();</script><button id="load-next-button" onclick="loadNextPage('https://jsonplaceholder.typicode.com/users/1')">Load more</button>`;
}
function htmlDecode(input) {
var e = document.createElement('div');
e.innerHTML = input;
return e.childNodes[0].nodeValue;;
}
function loadNextPage(uri) {
let xhr = new XMLHttpRequest();
xhr.open('GET', uri);
xhr.onload = function() {
if (xhr.status === 200) {
// Im' using responseTest you should use xhr.response
let rawdata = htmlDecode(getResponse());
clickCount++
// Script matches
matches = Array.from(rawdata.matchAll(regexScript));
let rawScript = matches[0][1];
let inlineScript = document.createTextNode(rawScript);
// Button matches
matches = Array.from(rawdata.matchAll(regexButton));
let rawButton = matches[0];
let script = document.getElementById('loaded-script');
script.innerHTML = '';
script.appendChild(inlineScript);
let loadNextButton = document.getElementById('load-next-button');
loadNextButton.parentNode.removeChild(loadNextButton);
let vbw = document.getElementById('vbw');
let doc = new DOMParser().parseFromString(rawButton, 'text/html');
//let script = doc.body.childNodes;
let button = doc.getElementById('load-next-button');
// vbw.innerHTML = vbw.innerHTML + xhr.response;
vbw.appendChild(button);
nodeCounter.innerHTML = 'Request processed: ' + clickCount;
eval(script.innerHTML);
} else {
conosole.log('Failed: ' + xhr.status);
}
};
xhr.send();
}
<div id="vbw">
<script id="loaded-script" type="application/javascript">
let thumbNailStore = {
concat: function() {
console.log('Concat original');
},
};
function addThumbnailListeners() {
console.log('addThumbnailListeners');
}
thumbNailStore.concat(); // This produces a JSON which is appended to an array in the <head> of the page
addThumbnailListeners();
</script>
<button id="load-next-button" onclick="loadNextPage('https://jsonplaceholder.typicode.com/users/1')">Load more</button>
</div>
<div id="counter"></div>
Upvotes: 1
Reputation: 2203
If jQuery is an option, load()
will execute any script elements contained in the loaded content as well as modify the inner HTML, which sounds like what you need.
Otherwise, you can use eval. You just need some way of retrieving the script element (perhaps the last script element child of vbw
, by some predetermined id, or even by creating an element out of the response data and accessing the script subElement from there). Either way, once you have the script element object:
eval(scriptElement.innterHtml);
Upvotes: 1