JavaScript und speziell die JQuery-Bilbliothek sind heute für attraktive Oberflächen auch SharePoint nicht mehr wegzudenken. Gerade aufgrund des eingeschränkten Customizings macht der Einsatz von JQuery unter Microsoft Office 365 / Online sehr viel Sinn, doch aufgepasst: Die Einbindung und Verwendung von JQuery kommt mit einigen Hürden. Dieser Beitrag zeigt (Ansätze), wie man diesen beikommt!
Ausgangslage und Einschränkungen
Dieser Blog-Beitrag befasst sich mit der Einbindung von der JQuery Bilbliothek und deren Verwendung im Rahmen von Sandboxed Solutions zur Verwendung z.B. unter Microsoft Office 365 / Online. Für eine Farm Solution ist die Einbindung wesentlich einfacher (Eigene MasterPage, Verwendung einer Delegate wie AdditionalPageHead oder eines Platzhalters etc.), ist aber hier nicht das Thema.
Ebenfalls wollen wir erreichen, dass die JQuery Bilbiothek mit jedem Seitenaufruf und unabhängig der MasterPage oder des verwendeten Seitenlayouts geladen wird.
Als allererstes muss man sich entscheiden, ob man die JQuery Bibliothek auf dem Zielsystem installieren will, oder ob man einen Link zum Beispiel auf Google's Content Delivery Network (CDN) verwendet und JQuery so jeweils aus dem Internet geladen wird.
JQuery einbinden - die Variante "lokal"
Für eine lokale Installation wird JQuery mit Hilfe eines Moduls z.B. in die "Style Library" oder in die "Assets Library" eingespielt, wobei dies eigentlich keine Rolle spielt, man kann auch einen beliebigen anderen Pfad wählen, der nicht unbedingt in einer Dokumenten-Bibliothek liegen muss (beliebt ist z.B. "/js").
Ein sehr gutes Beispiel für die lokale Installation findet sich
hier.
Das Modul:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="JQueryLibrary">
<File
Path="JQueryLibrary\jquery-1.4.2.min.js"
Url="SiteAssets/jquery-1.4.2.min.js"
IgnoreIfAlreadyExists="TRUE"/>
</Module>
</Elements>
Das JQuery-Script wird über eine
CustomAction geladen wird und im Attributt
ScriptSrc wird der Platzhalter ~
SiteCollection verwendet:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
ScriptSrc="~SiteCollection/SiteAssets/jquery-1.4.2.min.js"
Location="ScriptLink"
Sequence="10">
</CustomAction>
</Elements>
JQuery einbinden - die Variante "Google CDN"
Der Ansatz über die
CustomAction verwendet auch der 2. Ansatz, der JQuery vom Google CDN ladet und somit eine direkte Internetverbindung vom Client voraussetzt.
Das von mir verwendete Beispiel kommt von
hier.
Anstatt JQuery direkt einzubinden wird dieses über einen JavaScript-Block geladen:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Location="ScriptLink"
ScriptBlock="
//Function that Dynamically Loads the Javascript
function loadScript(scriptSrc, callbackFunction) {
//Gets a reference to the Head section, where the Script tage will be inserted
var headSection = document.getElementsByTagName('head')[0];
//Creates a new Script tag
var script = document.createElement('script');
script.type = 'text/javascript';
//Sets the source for the script tag to our external library
script.src = scriptSrc;
// most browsers
script.onload = callbackFunction;
// Fix to ensure support for IE 6-7
script.onreadystatechange = function() {
if (this.readyState == 'complete') {
callbackFunction();
}
}
//Adds the section to the header
headSection.appendChild(script);
}
//Function to test that JQuery has loaded successfully
//This should be replaced with your own function calls
function runScript() {
$(document).ready(function() {
alert('jQuery is loaded!!'); });
}
//Important, this ensure the scripts execute after SharePoint has done everything it needs to.
_spBodyOnLoadFunctionNames.push('loadScript(\'http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js\', runScript)');
" Sequence="101">
</CustomAction>
Dieser Ansatz ist an und für sich perfekt, setzt allerdings einen absoluten Pfad für die JQury-Bibliothek voraus, denn der Platzhalter ~
SiteCollection kann hier meines Wissens nicht verwendet werden. Allenfalls könnte man das ECMA-Script Framework von SharePoint verwenden und den Site-Collection URL über
SP.Site.get_url() herauslesen, das habe ich allerdings nicht probiert und eventuell gibt es auch ein Timing-Problem, da JQuery ja möglichst früh geladen werden soll.
Nun da wir JQuery erfolgrech unter Microsoft Office 365 / Online eingebunden haben, wollen wir die Bibliothek verwenden, was auch nicht ganz einfach ist, wie die folgenden Kapitel zeigen.
Probleme vermeiden: JQuery doppelt und dreifach geladen
Fast jede 3rdParty Komponente benötigt heutzutage JQuery und ladet die Bilbiothek auch gleich noch selber. Nicht selten hat man es dann mit mehreren, z.T. noch unterschiedlichen Versionen zu tun. Diesem Problem ist nicht so einfach beizukommen.
In der Regel mache ich ein (oft unsichtbares) Feature in meine Solutions und der jeweilige Admin kann selber entscheiden, ob er JQuery über dieses Feature laden lassen will oder ob es vielleicht bereits im Einsatz ist.
Einen sehr spannenden Ansatz finde ich den folgenden:
//Source: https://gist.github.com/902090
//Conditionally load jQuery
//inspired by http://www.smashingmagazine.com/2010/05/23/make-your-own-bookmarklets-with-jquery/
window.onload = function () {
if (typeof jQuery == 'undefined') {
var jQ = document.createElement('script');
jQ.type = 'text/javascript';
jQ.onload = jQ.onreadystatechange = myOnLoadEvent;
jQ.src = ( "https:" == location.protocol ? "https//" : "http//" ) + 'ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js';
document.body.appendChild(jQ);
} else {
myOnLoadEvent();
}
};
function myOnLoadEvent() {
jQuery(document).ready(function($) {
alert('your code here');
});
}
Mit diesem JavaScript (welches man z.B. mit dem obigen Script für die Einbindung kombinieren könnte) wird geprüft, ob JQuery schon geladen ist. Ist dies nicht der Fall, wird dies (wiederum über Google CDN) nachgehol.
Probleme vermeiden: .ready() - wann ist wer bereit?
Sehr viele JQuery-Scripts werden über die .ready() Methode ausgeführt, welche immer dann feuert, wenn die Seite vollständig geladen ist und bereit zum anzeigen ist.
Wird JQuery aber so wie oben beschrieben geladen, kann man nicht unbedingt vollständig sicher sein, dass die JQuery-Bilbliothek auch tatsächlich schon geladen ist, wenn die
.ready() Methode angesprochen wird, was zu einem Fehler führen kann.
Umgehen kann man dies, indem man anstatt der JQuery
.ready() Methode sein Script folgendermassen nach dem Laden der Seite ausführen lässt:
ExecuteOrDelayUntilScriptLoaded(doSomethingUseful, "sp.js");
function doSomethingUseful() {
// now you can safely use JQuery functions
$(".myButton").click(function{
//do some cool stuff
});
}
Mein Dank für diesen wichtigen Tip geht an folgende
Quelle.
Probleme vermeiden: $ überall und JQuery.noConflict()
In den allermeisten Fällen wird für JQuery Beispiele die $-Abkürzung für JQuery Funktionen wie Selektoren oder die berühmte
.ready() Funktion aufzurufen. Dumm nur, dass SharePoint diese Abkürzung an verschiedenen Stellen selber verwendet respektive "neu definiert" und es so aus unerklärlich scheinenden Gründen zu Konflikten kommen kann.
Diese Konflikte kann man am besten vermeiden, in dem man die die Methode
.noConflict() von JQuery aufruft und entweder einen neuen "ShortCut" definifiert oder anstatt dem $-Zeichen das Wort
jQuery verwendet:
$('.someClass') wird so zu
jQuery('.someClass')
Mehr dazu gibts
hier und
hier. Aber Achtung: unbedingt sicherstellen, dass noConflict() nur ein einziges Mal ausgeführt wird, sonst könnten plötzlich die Ribbons fehlen, was
hier beschrieben wird.
Probleme vermiden: AssetPortalBrowser.aspx und JQuery mögen sich nicht
Als Rückmeldung auf unseren Gratis-Download "Dynamisches Inhaltsverzeichnis für SharePoint Listen-Wikis" wurde uns geschrieben, dass es offenbar Probleme mit dem Asset-Picker, konkret dem "AssetPortalBrowser", und JQuery gibt. Warum dies so ist, kann ich nicht sagen, dafür habe ich mich zuwenig damit befasst, aber wie man
hier sieht, haben sich auch andere Leute damit beschäftigt und eine sehr schöne Lösung gefunden, um JQuery NICHT zu laden, wenn man sich auf der AssetPortalBrowser.aspx Seite befindet:
function loadjQueryScripts(src) {
if (window.location.href.indexOf('AssetPortalBrowser.aspx') > 0) return;
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
head.appendChild(script);
1}
Befor man sich aber mit solchen speziellen Lösungen befasst, sollte man unbedingt den obigen Typ befolgen und auf das $-Zeichen verzichten. Ohne es bewusst getestet zu haben bin ich der Meinung, dass dies das Problem mit dem AssetPortalBrowser bereits behebt.
Ich hoffe, dem einen oder anderen mit diesen Ausführungen helfen zu können und freue mich auf Euer Feedback und viele schöne JQuery Lösungen!
UPDATE 16.04.2012: Lokales JQuery inklusive noConflict() für Office 365/Online installieren
Mit folgenden beiden CustomActions kann eine lokale Instanz von JQuery (hier in der Style Library) mit einer Sandbox-/User-Solution deployed werden und es entstehen keine Konflikte, sofern anstatt die Abkürzung $ jQuery benutzt wird:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="JQuery" Url="Style Library/1stQuad" RootWebOnly="TRUE">
<File Path="JQuery\jquery-1.7.1.min.js" Url="JQuery/jquery-1.7.1.min.js" Type="GhostableInLibrary" />
</Module>
<CustomAction
ScriptSrc="~SiteCollection/Style Library/1stQuad/JQuery/jquery-1.7.1.min.js"
Location="ScriptLink"
Sequence="10">
</CustomAction>
<CustomAction
ScriptBlock="jQuery.noConflict();"
Location="ScriptLink"
Sequence="11">
</CustomAction>
</Elements>