I'm trying to use JavaScript to add a custom link to the suite bar in SharePoint (SharePoint 2016 on premise). The code works fine except for the first time I load the SharePoint site. Here it is:
<script>
var raiseFunc = function() {
var link = document.createElement('a');
var linktext = document.createTextNode("Google");
link.href = "http://www.google.ca"
link.setAttribute("class", "o365button o365cs-nav-appTitle o365cs-topnavText");
var span = document.createElement('span');
span.appendChild(linktext);
span.setAttribute("class", "o365cs-nav-brandingText");
link.appendChild(span);
var temp = document.getElementById("Sites_BrandBar");
temp.parentElement.appendChild(link);
};
_spBodyOnLoadFunctions.push(raiseFunc);
</script>
When I refresh the page or navigate around, everything works. It just doesn't work when I open the page from a new window or tab. (I have the MDS feature disabled.) It seems like the suite bar is not available when the code runs for the first time, even though I'm delaying my code using the _spbodyOnLoadFunctions technique.
Any ideas?
Update: On Matt's suggestion in the comments, I put in try/catch to get any errors. No errors are reported but I did notice a change in behaviour. I did about 20 tests in a row and sometimes the new link is added fine, sometimes it is not, and sometimes it is added for a second and then disappears.
I also tried putting an alert in the code before this line:
var temp = document.getElementById("Sites_BrandBar");
The delay is execution makes it work so clearly it is a timing thing. I assume that sometimes the suite bar is not ready when the code runs. I added a timeout as a test:
setTimeout(function(){
var temp = document.getElementById("Sites_BrandBar");
temp.parentElement.appendChild(link);
}, 500);
This fixes the issue but I don't like the idea of using a timeout every time the page runs.
Any ideas on how to delay the code execution until the suite bar is ready?
As per Matt's comment, this worked:
"Try to make your add link into a function and use SP.SOD.executeFunc('sp.js','SP.ClientContext', yourFunctionToInsertHere); I believe it's the sp.js that is not loading before you are trying to add it. This should delay your add until it's loaded."
Thanks!
I think it's being done using a Custom Action, but can't be certain.
One way to test it is to put the file into a .js file and then add it as a Custom Action using the excellent script, which John Liu built.
http://johnliu.net/blog/2015/12/the-safest-future-proof-way-to-brand-your-sharepoint-and-sharepoint-online
The other way is to maybe use the PnP PowerShell cmdlets using;
Add-PnPJavaScriptBlock -Name SuiteBar -Script '<your code here>' -Sequence 9999 -Scope Site
Related
I'm trying to create an iOS shortcut on my iPad using Run JavaScript on Web Page to configure a router when connected to its WiFi.
The functions work separately when I select a particular section of the router configuration web page before executing a shortcut. But what I need is changing the setup of the router by executing all the functions in one single script.
When I call 192.168.0.1 the first function would be for logging in, the next function for changing the network options, the next one for changing the security options etc. For this I apparently need setTimeout() to have time to select different pages in the menu. For some reason setTimeout() doesn't really work on iOS in Safari. It seems to be a known problem but I couldn't find any solutions or work arounds for the current iOS version yet.
I tried setTimeout(), setInterval() and exectuion of a function on condition like the following:
var starttime = 0;
var now = new Date().getTime();
if((now - starttime) > 2000) {...}
Here is a code snippet:
function defaultLogin(){
document.getElementByID('username').value = 'admin';
document.getElementByID('pcPassword').value = 'admin';
}
function clickBtn(){
document.getElementByID('loginBtn').click();
}
function changeLoginData{
window.frames['frame1'].contentDocument.documentElement.getElementsByClassName('T plus')[9].firstElementChildclick();
...
}
defaultLogin();
clickBtn();
setTimeout(changeLoginData, 1000);
completion();
I do not get any errors but the function changeLoginData() seems to be executed before logging in which is obviously pointless because i.e. the element ('T plus')[9] hasn't been loaded at that stage.
What am I missing?
Thank you in advance! Any help is much appreciated.
The last thread on this was asked in 2010 and, as far as I can tell, there is still incomplete (or no) support for the 'load' event of a element. My code:
function loadedCb(){ console.log("Loaded!"); };
function errorCb(){ console.log("Error!"); };
var file = document.createElement("link");
file.type = "text/css";
file.rel = "stylesheet";
file.addEventListener('load', loadedCb); // or: file.onload = loadedCb;
file.addEventListener('error', errorCb); // or: file.onerror = errorCb;
file.src = "theDir/theFile.css"; // should immediately start to load.
document.getElementsByTagName('head')[0].appendChild(file);
When this code is run in Firefox 49.0.2, neither callback is ever invoked (and the CSS file also never loads in at any point, which perhaps suggests I've done something wrong). The corresponding code for loading in a .js file as a <script> element works perfectly, however.
Is there any elegant solution to running a callback upon loading in (or erroring upon) a CSS file as of 2016? Or am I doing something basic wrong? I am not using JQuery in my project, incidentally.
Found the main problem: was setting src instead of href. Will check after lunch to see whether the events are firing, but at a brief glance, it looks to be working so far.
I'm building a Squarespace page, and I want to have an outgoing link on the page whose query parameters are set according to query parameters on the page itself. Since Squarespace embeds YUI3 in every site automatically, I'm trying to use that (even though I have more experience with jquery).
Example: The page is loaded as http://example.com/page/?email=foo#bar.com. I have a link on the page that goes to http://another.example.com/page which should be modified to go to http://another.example.com/page?address=foo#bar.com.
The following code does exactly what I need when I paste it in the browser console:
var MyButton = Y.all('a[href="http://another.example.com/page"]');
var QueryString = Y.QueryString.parse(window.location.search.substring(1));
MyButton.setAttribute('href','http://another.example.com/page?address=' + QueryString.email);
However, when I put that code in the page source, then when the page loads, the following error appears in the console: Uncaught TypeError: Y.QueryString.parse is not a function
My current theory is that YUI3 loads asynchronously in pieces, and this code runs at a point when Y.all is available but Y.QueryString.parse is not... is that right? What would be the best solution to this?
Yui3 is indeed built around asychonous modules loading, in this case you miss the querystring module.
You need to wrap your code with a Y.use call:
Y.use('querystring', function(Y) {
var MyButton = Y.all('a[href="http://another.example.com/page"]');
var QueryString = Y.QueryString.parse(window.location.search.substring(1));
MyButton.setAttribute('href', 'http://another.example.com/page?address=' + QueryString.email);
});
I have this tiny little script that I run inside Chrome using Tampermonkey and works great.
However, when I use it in Firefox with Greasemonkey, it shows up on the active list, meaning its matching the page but it doesn't actually execute the code. I know it has to be a simple something I am overlooking but its not hitting me.
var myVar=setInterval(function(){myTimer();},100);
function myStopFunction()
{
clearInterval(myVar);
}
function myTimer()
{
var p1 = "Login";
var p2 = "mode=login";
var x = document.body.innerHTML;
if (x.match(p1) && x.match(p2)){
document.documentURI = "/ucp.php?mode=login";
}
myStopFunction();
}
Script Logic/Function
I am using a timer to prevent the script from triggering over and over in a permanent loop.
It simply detects if I am logged into a phpBB forum or not, if not send me to the login page so I can log in.
I am using document URI so that the location of the original is preserved so upon login, it takes me right back to it.
Often phpBB when you log in, it will take you back to the index page so this preserves my original intent of going to the actual link.
This script works perfectly and as expected on Chrome using TM but on Firefox using GM it doesn't trigger, am I missing something here?
From the Firefox spec:
(document.documentURI)
Returns the document location as string. It is read-only per DOM4 specification.
And, indeed, the latest spec still specifies that this attribute must be read only.
If Chrome lets you write this property, then that is non-standard behavior and maybe a bug.
Use location.assign(), or location.replace(), or just programmatically click the login button -- which often preserves the target page.
I know that for safety reasons that this is not easy to achieve, however there would be a way to do so as firebug does...
Please help, would like to invoke some script in the page's context to achieve some effect...
Basically, I would like to achieve two functionality:
1. add jQuery to any web page automatically if not already exist.
2. when open certain address, call a method of that page to auto notify the server. (an ajax functionality of the page)
I have tried to inject on the body, no luck.
tried to get the window object, which however do not have access to call the function.
Will try to change the location to something like: javascript:alert('test inject');
Many thx.
OK, after reading some official documentation and the GreaseMonkey's source, I get the following method which basically works for me.
Hope it will save sb's hour:
var appcontent = document.getElementById("appcontent"); // browser
if (appcontent) {
appcontent.addEventListener("DOMContentLoaded", function (evnt) {
var doc = evnt.originalTarget;
var win = doc.defaultView;
var unsafeWin = win.wrappedJSObject;
// vote.up is the function on the page's context
// which is take from this site as example
unsafeWin.vote.up(...);
}, true);
}
}
Greasemonkey does that. If you are developing your own extension with similar functionality, you can use Components.utils.evalInSandbox.