Michael Hofer

Michael Hofer
11.04.2012
0 Kommentar(e)

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() { &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
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>


Kommentar hinterlassen




 Security code

Kommentar(e)

Noch keine Kommentare.