Our website uses JS to do partial page hydration to avoid downloading the entire page when navigating within the website. We have been getting reports from Safari browser users that when they click a nav link, they loose all the content (but the header and footer are still there).
All pages have a full static html version (for when they are landed on) and a json file with the content specific to that page.
Our JS script intercepts internal links, fetches the appropriate JSON file then parses the JSON data inserting the appropriate content in its correct spot. We are using the older style XMLHttpRequest rather than fetch to assist older technology. The system works on all the browsers we have access to (mostly Windows and Android based).
The reports from folks seem to be older versions 14 & 15, not the latest. But our customers are not always computer savvy, so haven't been able to get any detailed info.
Our Javascript is below. The process gets called as part of a page wide event propagation script on all A links:
// what is left should be used for page hydration
else {
// determine target page name, then convert to json file name
const tpath = url.pathname;
const pgpath = tpath.replace('.htm', '.json');
const pgurl = bspgurl + pgpath;
// trigger page fetch and hydration
getNewPage(pgurl, tpath);
}
The getNewPage:
// fetch json from server, hydrate page with new content
function getNewPage(url, tpath) {
const xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// scroll to top of page
window.scroll({
top: 0,
left: 0,
behavior: 'instant',
});
// insert new page content
const myArr = JSON.parse(this.responseText);
putNewPage(myArr);
// update browser history
saveToHstry(myArr, tpath);
}
};
xmlhttp.open('GET', url, true);
xmlhttp.send();
}
The putNewPage
// hydrate page with new content
function putNewPage(data) {
const m = document.getElementsByTagName('meta');
const l = document.getElementsByTagName('link');
const p = document.getElementsByTagName('input');
for (let i = 0; i < data.length; i++) {
switch (data[i].fncn) {
case 'body':
var bdy = document.getElementsByTagName('body');
bdy[0].setAttribute('id', data[i].cntnt);
pgType = data[i].cntnt;
break;
case 'bkgnd':
document.body.className = '';
document.body.classList.add(data[i].cntnt);
break;
case 'title':
document.title = data[i].cntnt;
break;
case 'metan':
for (var j = 0; j < m.length; j++) {
if (m[j].getAttribute('name') === data[i].trgt) {
m[j].setAttribute('content', data[i].cntnt);
}
}
break;
case 'metap':
for (var j = 0; j < m.length; j++) {
if (m[j].getAttribute('property') === data[i].trgt) {
m[j].setAttribute('content', data[i].cntnt);
}
}
break;
case 'link':
for (let k = 0; k < l.length; k++) {
if (l[k].getAttribute('rel') === data[i].trgt) {
l[k].setAttribute('href', data[i].cntnt);
}
}
break;
case 'id':
$(data[i].trgt).innerHTML = data[i].cntnt;
break;
case 'input':
for (let f = 0; f < p.length; f++) {
if (p[f].getAttribute('name') === data[i].trgt) {
p[f].setAttribute('value', data[i].cntnt);
}
}
break;
case 'js':
window[data[i].trgt] = data[i].cntnt;
break;
}
}
}
The website pages are all pre-rendered and saved on a CDN.
Related
var infiniteResponseAAA= {
Poodwaddle_com: '"Poodwaddle.com", infiniteResponse[1].children[language].children[17].innerHTML, "Images\poodwaddle.com.jpg", searchBResponse[1].children[17].textContent',
SystemRequirementsLab_com: '"System Requirements Lab.com", infiniteResponse[1].children[language].children[18].innerHTML, "Images\systemrequirementslab.com.jpg", searchBResponse[1].children[18].textContent',
BrowsePad: '"BrowsePad.org", infiniteResponse[1].children[language].children[19].innerHTML, "Images\BrowsePad.org.jpg", searchBResponse[1].children[19].textContent',
SMMRY_com: '"SMMRY.com", infiniteResponse[1].children[language].children[20].innerHTML, "Images\smmry.com.jpg", searchBResponse[1].children[20].textContent',
Whatthefuckshouldimakefordinner_com: '"Whatthefuckshouldimakefordinner.com", infiniteResponse[1].children[language].children[21].innerHTML, "Images\Whatthefuckshouldimakefordinner.com.jpg", searchBResponse[1].children[21].textContent',
Coolors_co: '"Coolors.co", infiniteResponse[1].children[language].children[22].innerHTML, "Images\Coolors.co.jpg", searchBResponse[1].children[22].textContent',
Numbeo_com: '"Numbeo.com", infiniteResponse[1].children[language].children[23].innerHTML, "Images\Numbeo.com.jpg", searchBResponse[1].children[23].textContent',
Doesthedogdie_com: '"Doesthedogdie.com", infiniteResponse[1].children[language].children[24].innerHTML, "Images\Doesthedogdie.com.jpg", searchBResponse[1].children[24].textContent',
Online_Convert_com: '"Online-Convert.com", infiniteResponse[1].children[language].children[25].innerHTML, "Images\Online-Convert.com.jpg", searchBResponse[1].children[25].textContent'
}
var infiniteResponse= Object.keys(infiniteResponseAAA).sort();
var sitesOnPage = [];
var brLength= document.querySelectorAll("br").length;
var whereToAddIt= document.querySelectorAll("br")[brLength-1];
var observer = new IntersectionObserver(function(entries){
entries.forEach(function(entry) {
if (entry.isIntersecting) {
getDataInfiniteS();
}
});
});
observer.observe(whereToAddIt);
// Data checker
function getDataInfiniteS() {
InfiniteScrolling();
}
// Actual script
function InfiniteScrolling() {
var code;
// It gets the first 5 sites that are already on the page and puts them inside sitesOnPage
if (sitesOnPage[0] === undefined) {
var h2s= document.querySelectorAll("h2");
infiniteResponseAAA[infiniteResponse[0]].split(",")[0].replace(/["]/g, "");
for (var i=0; i < h2s.length; i++) {
if (h2s[i].innerHTML === infiniteResponseAAA[infiniteResponse[i]].split(",")[0].replace(/["]/g, "")) sitesOnPage.push(i);
}
}
function randomSiteInfosF(siteN) {
// Checks if it's a number and not a string or something else
if (typeof siteN !== "number") return;
siteChecker_RNG(siteN)
// Infos about the site: title
eval(`siteInfo${siteN}= infiniteResponseAAA[infiniteResponse[${eval(`randomSite${siteN}`)}]].split(",")`);
eval(`siteTitle${siteN}= siteInfo${siteN}[0].replace(/["]/g, "")`);
}
function siteChecker_RNG(siteN) {
var sitesAvailable= infiniteResponse.length;
var h2s= document.querySelectorAll("h2");
var randomSiteN= Math.floor(Math.random() * sitesAvailable);
// Return if all sites are loaded
if (h2s.length === sitesAvailable) return;
// Checks if the random number is a correct one
for (var i= 0; i < sitesOnPage.length; i++) {
if (randomSiteN === sitesOnPage[i]) {
siteChecker_RNG(siteN)
} else continue;
}
eval(`randomSite${siteN}= randomSiteN`);
}
var howManySitesPerLoad= 3;
for (let i= 0; i < howManySitesPerLoad; i++) {
randomSiteInfosF(i);
}
// Creates a new div for the sites loaded from the database
if (document.getElementById("InfiniteScrollingID") === null) {
whereToAddIt.insertAdjacentHTML("afterend", "<div id='InfiniteScrollingID'></div>");
}
for (let i= 0; i < howManySitesPerLoad; i++) {
code= `${eval(`siteTitle${i}`)}\n</br></br></br>`
document.getElementById("InfiniteScrollingID").insertAdjacentHTML("beforeend", code);
}
for (var i=0; i < howManySitesPerLoad; i++) {
sitesOnPage.push(eval(`randomSite${i}`))
}
brLength= document.querySelectorAll("br").length;
whereToAddIt= document.querySelectorAll("br")[brLength-1];
observer.disconnect()
observer.observe(whereToAddIt)
}
This is a re-post since nobody answered me before when i provided a working minimal example and this script is super important for my site.
This is part of code that is used for my site in its sections, http://www.coolwebsites.ml/
https://youtu.be/QDrn_9svt6Y
CodePen: https://codepen.io/Attisalva/pen/pojdZXr?editors=1010
In the CodePen example (same problem), it creates (in this case title names to simplify the code) some title names that are already on the page and it should only create titles that aren't yet created (you can find titles in the object "infiniteResponseAAA").
I'm trying to lazy loading new images, titles, descs, etc randomly on the page but it keeps creating without checking properly if everything is already on page.
It's because i didn't end/return the function after i got a good number
searchURL: function() {
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
var link, url, parser, newPathName = '',
emailUrl = /\/img\//,
newstr = '',
doc = document,
container,
container_id,
container_links,
container_images,
documentTableWrapper,
docBodyFirstChild,
nodeToTargetToInsertLP;
if (!doc.getElementById('container')) {
container = doc.createElement('div');
container.setAttribute('id', 'container');
container.className = 'container-avon-representative-news';
container_links = container.getElementsByTagName('a');
container_id = doc.getElementById('container');
docBodyFirstChild = doc.body.firstChild;
nodeToTargetToInsertLP = doc.getElementsByClassName('flexTile')[4];
if (nodeToTargetToInsertLP) {
documentTableWrapper = doc.getElementsByClassName('marginfix')[0];
container.appendChild(documentTableWrapper);
insertAfter(container, nodeToTargetToInsertLP);
} else {
documentTableWrapper = doc.getElementsByTagName('table')[0];
container.appendChild(documentTableWrapper);
doc.body.insertBefore(container, docBodyFirstChild);
}
} else {
container_links = doc.getElementById('container').getElementsByTagName('a');
}
container_images = container.getElementsByTagName('img');
for (var i = 0; i < container_images.length; i++) {
if (arguments[0] == "smo" || arguments[1] == "smodev") {
container_images[i].src = container_images[i].src.replace(emailUrl, '/images/dir_a/');
} else {
container_images[i].src = container_images[i].src.replace(emailUrl, '/static/images/dir_b/');
}
}
for (var i = 0, len = arguments.length; i < len; i++) {
url = getURL(arguments[i]);
for (var j = 0, jlen = container_links.length; j < jlen; j++) {
link = container_links[j];
if (link.href.indexOf(url) !== -1) {
parser = document.createElement('a');
parser.href = link.href;
link.setAttribute('target', '_self');
newPathName = parser.pathname;
if (newPathName.search(/Executive|District|Division|National/) != -1) {
newPathName = newPathName.split('/').pop();
newstr = newPathName;
} else {
newstr = newPathName;
}
link.href = newstr;
} else {
link.setAttribute('target', '_blank');
}
}
}
},
I have a loop which is going through the images which are children of a div:
var container_images = container.getElementsByTagName('img'),
emailUrl = /\/img\//;
for (var i = 0; i < container_images.length; i++) {
if (arguments[0] == "smo" || arguments[1] == "smodev") {
container_images[i].src = container_images[i].src.replace(emailUrl, '/images/dir_a/');
} else {
container_images[i].src = container_images[i].src.replace(emailUrl, '/static/images/dir_b/');
}
}
Strangely, this is working in one instance (another area of the server), but in another instance it's adding other pathnames/directories which I didn't even indicate?!
I figured replace is explicit enough:
xxx.src.replace(emailUrl, '/images/dir_a/');
If this image tag has its source attribute set to /\/img\// replace it with /images/dir_a/
Does anyone know a method which is more explicit and won't add directories which I didn't assign?
And could anyone explain how this is happening with the replace method?
UPDATE
To give some background the environment where I am not having any problems is as follows: My company uses a CMS to serve these pages. The file extension is called .page; I have never heard of that extension outside the use of the CMS so I supposed it's proprietary to the CMS.
Anyway, in the CMS you can also server html files like you would from any other server. But in this instance, when I check the source in dev tools, it seems to add <img src="http://xxx.xxx.com/REPSuite/static/html/inews_archives/static/images/dir_b/xxx.png"> instead of <img src="http://xxx.xxx.com/REPSuite/static/images/dir_b/xxx.png">
The issue is you are using .src and you want to be using .getAttribute('src') and .setAttribute('src', 'your new src').
HTMLImageElement.src
Is a DOMString that reflects the src HTML
attribute, containing the full URL of the image including base URI.
so when you call .src it gives you the full absolute current uri. You just want to get/set the src attribute. working example jsbin
The answer turned out to be so simple (of course after the fact). Anyway—because I was getting that funky path only in the other environment where the page was getting served, I simply made a conditional which checked for that funky path.
So instead of checking one regex I checked for two.
var emailUrl = /img\//gi,
tsUrl = /\/REPSuite\/static\/html\/inews_archives\/img\//gi;
for (var i = 0; i < avon_rep_container_images.length; i++) {
if (arguments[0] == "smo" || arguments[1] == "smodev") { // Checks what environment the page will be used in so we can change the src attribute of all the images in the document to the appropriate path
avon_rep_container_images[i].src = avon_rep_container_images[i].src.replace(emailUrl, '/images/avon_manager_news/');
} else if(!nodeToTargetToInsertLP) {
avon_rep_container_images[i].src = avon_rep_container_images[i].src.replace(tsUrl, '/REPSuite/static/images/rep_news/');
} else {
avon_rep_container_images[i].src = avon_rep_container_images[i].src.replace(emailUrl, '/static/images/rep_news/');
}
}
Thanks to everyone who helped out, I appreciate it!
I've got an issue with the SharePoint PeoplePicker control. When a Modal dialog is open and in Edit mode, it's possible to use the builtin function SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK) to refresh the page without losing changes in data. Fields like Text, Note, Choice, Date etc. works fine and their changes are not lost when SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK).
But PeoplePicker fields unfortunately loses their data!
Steps to reproduce
Create simple item with a user field
Make item open in dialog (List Settings - Advanced - Launch forms in dialog)
Create an item
Open the EditForm of the item
Update the people picker field
Execute SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK) in a console
Made a fix by storing PeoplePicker values in sessionStorage upon RefreshPage() and reload them afterwards.
TypeScript 1.8
// FIX for SPClientPeoplePicker not preserving state afre SP.UI.ModalDialog.RefreshPage...
export class ClientPeoplePickerFix {
static getPplPickerElementsInForm = (): Array<HTMLInputElement> => {
var result = new Array<HTMLInputElement>();
var elements = document.getElementsByTagName("form")[0].elements;
for (var i = 0; i < elements.length; i++) {
var currentEl = (<HTMLInputElement>elements[i]);
if (currentEl.name.indexOf("$ClientPeoplePicker_HiddenInput") > -1) {
result.push(currentEl);
}
}
return result;
}
static storePeoplePickerValues = (): void => {
var elements = ClientPeoplePickerFix.getPplPickerElementsInForm();
for (var i = 0; i < elements.length; i++) {
var currentEl = elements[i];
var pplPicker = SPClientPeoplePicker.PickerObjectFromSubElement(currentEl);
var currentUserInfo = pplPicker.GetAllUserInfo();
if (currentUserInfo.length > 0) {
sessionStorage.setItem(currentEl.name, JSON.stringify(currentUserInfo));
}
}
}
static restorePeoplePickerValues = (): void => {
var elements = ClientPeoplePickerFix.getPplPickerElementsInForm();
for (var i = 0; i < elements.length; i++) {
var currentEl = elements[i];
var currentUserInfoSerialized = sessionStorage.getItem(currentEl.name);
if (currentUserInfoSerialized) {
var pplPicker = SPClientPeoplePicker.PickerObjectFromSubElement(currentEl);
var currentUserInfo = JSON.parse(currentUserInfoSerialized);
currentUserInfo.forEach((user) => {
pplPicker.AddProcessedUser(user, true);
});
sessionStorage.removeItem(currentEl.name);
}
}
}
static overrideRefreshPage = (): void => {
var _refreshPage = SP.UI.ModalDialog.RefreshPage;
SP.UI.ModalDialog.RefreshPage = function () {
if (arguments && arguments.length > 0 && arguments[0] === SP.UI.DialogResult.OK) {
ClientPeoplePickerFix.storePeoplePickerValues();
}
_refreshPage.apply(this, arguments);
}
}
}
// Override SP.UI.Modal.Dialog.RefreshPage
// Store values from all SPClientPeoplePickers in sessionStorage when SP.UI.Modal.Dialog.RefreshPage is called
SP.SOD.executeOrDelayUntilScriptLoaded(() => {
ClientPeoplePickerFix.overrideRefreshPage();
}, "sp.ui.dialog.js");
// On page load
// Restore SPClientPeoplePicker values from sessionStorage and add in appropriate controls
SP.SOD.executeOrDelayUntilScriptLoaded(() => {
ClientPeoplePickerFix.restorePeoplePickerValues();
}, "sp.core.js");
This My First Question Hear In Stack Overflow, And I Wish To Get The Help I Expect..
I Was Looking For A Way To Change All The Firefox Bookmarks Titles
To 'TitleCase' .
I've already found a perfect technique to change it to UPPERCase using a fierfox Dev. tool
called "scratchpad" in the browser environment .
here's the steps for it:-
open about:config
set devtools.chrome.enabled to true
tools > web developer > scratchpad
environment > browser
edit > paste (i.e. copy and paste code below)
execute > run
Code.
function getChildIds(aRootNode, aChildIds) {
for (var i = 0; i < aRootNode.childCount; i++) {
var node = aRootNode.getChild(i);
aChildIds.push(node.itemId);
if (node.type == node.RESULT_TYPE_FOLDER) {
node.QueryInterface(Ci.nsINavHistoryContainerResultNode);
var oldContainerOpen = node.containerOpen;
node.containerOpen = true;
getChildIds(node, aChildIds);
node.containerOpen = oldContainerOpen;
}
}
}
var bs = Cc["#mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
var hs = Cc["#mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var query = hs.getNewQuery();
var options = hs.getNewQueryOptions();
options.queryType = options.QUERY_TYPE_BOOKMARKS;
var folders = new Array();
folders.push(bs.bookmarksMenuFolder); // Bookmarks Menu
folders.push(bs.toolbarFolder); // Bookmarks Toolbar
folders.push(bs.unfiledBookmarksFolder); // Unsorted Bookmarks
var childIds = new Array();
for (var i = 0; i < folders.length; i++) {
query.setFolders(folders.slice(i, i + 1), 1);
var result = hs.executeQuery(query, options);
var rootNode = result.root;
rootNode.containerOpen = true;
getChildIds(rootNode, childIds); // recursive function
rootNode.containerOpen = false;
}
bs.runInBatchMode({
runBatched: function () {
for (var i = 0; i < childIds.length; i++) {
var type = bs.getItemType(childIds[i]);
if (type == bs.TYPE_BOOKMARK || type == bs.TYPE_FOLDER) {
var title = bs.getItemTitle(childIds[i]);
if (title) {
bs.setItemTitle(childIds[i], title.toUpperCase());
}
}
}
}
}, null);
So, My Question Is, How To Modify That Code In Order To Change The Bookkmarks' Titles
To TitleCase (Capitalize Each Word) ?
Thanks In Advance...
See this website:
http://msdn.microsoft.com/en-us/library/system.globalization.textinfo.totitlecase(v=vs.110).aspx
You can use the method ToTitleCase from the TextInfo class to change a string to title case. (Make sure that the title variable is a string.) Then, instead of using title.toUpperCase use title.ToTitleCase.
I hope this helps,
Santiago
I want to write a sample extension to override newtab page with some html content provided from javascript. How to do this ?
Javascript to provide html content here :
function getFavouriteWebsite() {
var historyService = Components.classes["#mozilla.org/browser/nav-history-service;1"].getService(Components.interfaces.nsINavHistoryService);
var query = historyService.getNewQuery();
var options = historyService.getNewQueryOptions();
options.queryType = 0;
options.sortingMode = options.SORT_BY_VISITCOUNT_DESCENDING;
options.maxResults = 6;
var result = historyService.executeQuery(query, options);
result.root.containerOpen = true;
var contentHtml ='';
for (var i = 0; i < result.root.childCount; i++) {
for (var i = 0; i < result.root.childCount; i++) {
contentHtml += "<p>" + (result.root.getChild(i).title == "" ? result.root.getChild(i).uri : result.root.getChild(i).title) + "</p>";
}
}
return contentHtml;
}
To override the newtab page, set the browser.newtab.url preference to some HTML/XUL document. Include whatever scripts in that document to populate the content.
But please do not string-concatenate html fragments together as you'd likely end up with insecure code, but use DOM APIs such as document.createElement(), Node.textContent, Node.setAttribute etc instead.