← Ta mig hem

Kategori "jQuery"

$.append i Internet Explorer 7

Thursday, 14 October, 2010 - Inga kommentarer

Gör inte såhär:


var span = $("<span class=\"error\">").text("No u");
$("#errorcontainerthing").append(span);

Det fungerar inte i Internet Explorer 7 (och antagligen inga versioner under). Gör såhär istället:


var span = $("<span>").addClass("error").text("No u");
$("#errorcontainerthing").append(span);

Det borde fungera fint. Nu har jag sparat en timme i ditt liv.

Anonyma funktioner i JavaScript

Monday, 11 October, 2010 - Inga kommentarer

Anonyma funktioner kan man antingen älska eller hata. Eller tycka om sådär, eller inte bry sig om, eller, om man jobbar med JavaScript, bara använda utan att man själv vet om det.
I jQuery används de flitigt som callbackfunktioner som körs när event har triggats, och har man lärt sig jQuery genom tutorials och inte riktigt hänger med kan man sitta och använda anonyma funktioner utan att ens tänka på det.


function foo() { return "Namngiven funktion"; };
var foo = function() { return "Variabel som tilldelas en anonym funktion som värde"; };

Till och med när man använder sig av jQuery’s event som triggas när DOMen är färdigladdad använder man en anonym funktion:


$(document).ready(function(){
    //DOM färdigladdad, inne i en anonym funktion
});

$(function(){
   //Gör exakt samma sak som koden ovan, men ger +12 sexy till ditt script
});

En av de första “avancerade” sakerna man lär sig i jQuery är att om man skickar med en anonym funktion till jQuery-konstruktorn så är det samma sak som att knyta document-objektet till ready-eventet.

Anonyma funktioner är alltså väldigt användbara när man ska utföra något en gång, och kanske inte behöver använda logiken i funktionen någon annanstans, eller när man jobbar med objekt och vill tilldela en funktion till en property.


//Utan att använda anonyma funktioner:
function Person(name, beard, greeting){
    this.name = name;
    this.beard = beard;
    this.greeting = greeting;
    function sayHello() {
        log(this.greeting);
    }
    this.sayHello = sayHello;    //Skapar en funktion enbart för att tilldela den till en property
}

//Man borde istället skriva såhär:
function Person(name, beard, greeting){
    this.name = name;
    this.beard = beard;
    this.greeting = greeting;
    this.sayHai = function() {    //En anonym funktion tilldelas till this.sayHai
        log(this.greeting);
    };
}

Som alltid med JavaScript kan det vara förvirrande till en början, men om man tänker på funktioner som objekt istället för funktioner kan det bli enklare att förstå.


//Tänk såhär:
var num = 230; //Är ett namngivet Number-objekt

alert(230);    //Alert-funktion med ett anonymt Number-objekt som har värdet 230
alert(num);    //Alert-funktion med en namngiven variabel

setTimeout(function(){
    alert("hai");
}, 2000);    //En anonym funktion skickas in som parameter till setTimeout och körs efter två sekunder

function oh(){
    alert("hai");
};

setTimeout(oh, 2000);    //Gör samma sak som koden ovan, fast i det här fallet används en namngiven funktion.

Praktiskt exempel av closure-funktion

Friday, 8 October, 2010 - Inga kommentarer

Jag har stött på en honest-to-god användbar funktion som använder sig av closure och något som kallas currying. Jag påstår inte att jag förstår teorin bakom, men när det gäller klientsidevalidering i JavaScript hittade jag en användning för det.
Det kan ibland vara nödvändigt att validera formulär på klientsidan med hjälp av JavaScript, och det kan vara värt att skriva ett generellt valideringsobjekt med funktioner som man använder sig av, istället för att skriva omständliga valideringsregler per inputfält.
validation-objektet bör vara del av något större, med vettiga closures. Det här är bara ett exempel.

window.validaton = {
     //Returnerar olika funktioner beroende på om max är satt
    length: function(min, max){
        return function(input){
            if (max) {
                return function(input) {
                    if ($(input).val().length < min || $(input).val().length > max) {
                        return false;
                    }
                    return true;
                }
            }
            return function(input) {
                if ($(input).val().length < min) {
                    return false;
                }
                return true;
            }
        }
    }
}

Det här är praktiskt sett en funktionsgenerator. När man skriver sina valideringsregler för inputfält kan det se ut såhär:

//Skapar en ny funktion för att se så värdet är längre än 5 men annars hur långt som helst.
var isLongEnough = validation.length(5); 

if (isLongEnough("#AwesomeTextInput")) { //Är man inne i en bind-funktion räcker det med this här
    log("Everything went better than expected");
} else {
   log("Needs atleast 5 characters");
}

//Alternativt
 //Anonym funktion skapas och anropas, inte reusable

if (validation.length(5)("#AwesomeTextInput")) {
    log("Everything went better than expected");
} else {
   log("Needs atleast 5 characters");
}

Tack vare closures och svart magi vet den returnerade funktionen isLongEnough vad min är satt till.
Enligt mig är det här ett snyggare sätt än att skriva specialanpassade valideringsregler för varje inputfält, plus att man får användning av currying och closures, vilket alltid är trevligt.

Håll den globala namnrymden ren

Friday, 1 October, 2010 - Inga kommentarer

Mina <pre>-taggar spiller över lite, så jag ska lösa det på något awesome sätt. Under tiden fungerar piljäveln som ligger uppe till höger för att förstora utrymmet :P

JavaScript har inte, som alla andra vanliga programmeringsspråk, block-level-scope och det är inte alltid tydligt på vad this pekar på. Istället använder sig JavaScript av scope på funktionsnivå, som kan förvirra när man börjar utveckla. För att koka ner det så långt jag kan:

function hello(){
    var foo = "bar";
}
hello();
alert(foo); //undefined

Eftersom att foo deklareras inne i funktionen hello() så har inte scopet utanför funktionen möjlighet att se foo. Det är alltså skillnad på lokalt scope och globalt scope (window-objektet).
När man utvecklar plugins för jQuery kan det ibland vara värt att skapa variabler utanför pluginets scope, men ändå dölja det från det globala scopet, för att undvika konflikter, överskrivna variabler och ett onödigt stort window-namespace.
Det enklaste sättet att göra det på är att lägga hela sitt plugin, med de variabler man behöver, i en anonym självanropande funktion.

(function($, window) { //http://antaru.org/self-invoking-functions-i-javascript
    //jQuery-plugins

    var foo = "bar"; //Variabel som kan kommas åt av alla plugins i scopet

    //Plugin utan closure
    $.fn.superPlugin = function() {
        //Pluginfunktionalitet
    }; 

    (function(){
        var hurr = "durr"; //Variabel som enbart existerar i det lokala scopet
        console.log(foo); //Skriver ut "bar"

        //Plugin med closure
        $.fn.closurePlugin = function() {
            //Pluginfunktionalitet
        };
    })();

    console.log(hurr); //undefined    

})(jQuery, this);

Man skulle kunna göra separata “jQuery”-closures för varje plugin, men jag gillar att ha dem samlade och sedan var för sig, om det skulle krävas, ha ett eget closure; sin egna lilla värld.
Att definiera funktioner och globala variabler i JavaScript är oftast ingen bra idé.

Tips till alla Firebug-användare

Kör det här någon gång i början av era scripts:

window.log = function() {
    log.history = log.history || [];
    log.history.push(arguments);
    if (this.console) {
        console.log(Array.prototype.slice.call(arguments));
    }
};

De som har utvecklat för Firefox och använt Firebugs härliga console.log-funktion, sen testat det i någon annan webbläsare borde känna till problemet när funktionen anropas. Det går sönder. funktionen ovan löser det; plus, om den skapas den direkt kan man anropa log-metoden precis när som helst, och det går inte sönder, även om man råkar släppa koden skarpt.
Scriptet hittade jag på Paul Irish hemsida.
Edit: Testade lite mer, och att kunna anropa log.history i firebug är faktiskt riktigt sweet.

Associativa arrays är awesome

Thursday, 30 September, 2010 - Inga kommentarer

I JavaScript är allting objekt; strängar är objekt, siffror är objekt, till och med funktioner är objekt. Det härligaste objektet som finns är dock associativa arrayer, eller arrayer med en angiven nyckel istället för bara ett heltalsindex. Den enklaste formen av ett sånt här array är ett objekt med nycklar och värden tillsatta till den nyckeln. (Key, Value-pair för de som vill ha den tekniska termen)

var oh= { hai: "lol" };

Här skapas ett nytt objekt oh som innehåller en nyckel hai som har värdet “lol”. För att senare komma åt värdet med nyckeln hai finns det en del sätt:

oh.hai; //fungerar
oh["hai"] //fungerar
oh[0] //fungerar inte

Det andra sättet att hitta värdet kan vara bra om nyckeln innehåller punkter, mellanslag eller andra konstiga tecken som skulle förstöra scriptet.

Syntaxen är väldigt enkel, skapa en variabel, indikera att det är ett objekt med måsvingar ({}), sen ange värdet på nycklar i objektet med kolon istället för likamedtecken. Det är även möjligt att lagra objekt i objekt, och funktioner i nycklar, vilket gör det hela än mer awesome.
Större objekt:

var oh = {
    hai: "lol",
    foo: "bar",
    sayHi: function(){
        alert("Hi");
    },
    hello: {
        world : "Hello World"
    }
};

Med det här objektet kan man säga hej till världen genom oh.hello.world, eller bara säga hej genom oh.sayHi().

jQuery tar tillvara på associativa arrayer på väldigt många ställen, som exempelvis css()-funktionen, eller bind(), vilket gör det mycket lättare att manipulera utseendet och uppförandet av DOM-element.
Kod för att knyta multipla events till ett element:

$("#foo").bind({
    "mouseenter": function(){
        //Funktionalitet för hover in
    },
    "mouseleave": function(){
        //Funktionalitet för hover ut
    },
    "click": function(){
        //Funktionalitet för musklick
    }
});

Associativa arrays är awesome.

Härlig villkorssyntax i JavaScript

Wednesday, 29 September, 2010 - Inga kommentarer

På grund av JavaScripts flexibla natur så kan man få till riktigt snygga villkorssatser när man exempelvis hanterar variabler.

Säg till exempel att man vill sätta ett värde på en variabel beroende på om något existerar; och om det inte existerar sätta ett standardvärde på variabeln. Det skulle gå att lösa såhär (Använder jQuery också, det gör det lättare att läsa och förstå imo):

var input = $("#foo");

if (input.val() != "" && input.val().length > 0) {
    var anObject = input.val();
}
else {
    var anObject = "DefaultValue";
}

Inte så jättesnyggt. Inte så rätt heller, men det illustrerar min poäng. Det finns säkert någon som skulle ha skrivit det såhär. Jag personligen skulle ha skrivit det såhär:

var anObject = $("#foo").val() || "DefaultValue";

Antagligen hade jag cachat jQuery-objektet i en variabel, om det var del av en större funktion, men återigen så lyser min poäng igenom. Det magiska ligger i “||” som helt enkelt säger “Sätt värdet på anObject till det på min högra sida om det på min vänstra sida är false-ish, annars ta det på min vänstra sida”.

Med false-ish innebär det i det här fallet att om inputfältet är tomt kommer anObject tilldelas DefaultValue. För att ta hänsyn till om man försöker skicka in en tomsträng skulle man kunna anropa trim() på jQuery-objektet.

Jag vet inte riktigt vad den här tekniken heter, men jag gillar den verkligen.

Man skulle teoretiskt sett kunna göra såhär också:

var x = a || b || c || d || e;

Och x skulle få värdet av det första som kan omvandlas till “true”, det vill säga det första värdet som inte är null, false, undefined, tomsträng eller siffran noll.

Vill man gå ett steg längre skulle man säkert kunna använda sig av funktioner med egen logik som returnerar false-ish beroende på yttre faktorer. Tänk exempelvis om a i föregående exempel var ett funktions-objekt som kollade om användaren kör Firefox. Det skulle vara häftigt. Om funktionen ens skulle köras… Kanske kunde man lägga in en anonym funktion där…
Känns som att jag måste experimentera lite mer.
Jag gillar JavaScript <3

Self invoking functions i JavaScript

Monday, 27 September, 2010 - Inga kommentarer

Upptäckte en sexig lösning när jag fick i uppgift att blåsa upp en iframe till 100% av en sidas höjd (100% – en topbanners höjd). Själva koden för att göra det är inget speciellt, men det vackra i det hela är att använda en självanropande funktion för att skapa funktionen som ska användas för att ändra storlek på iframen.

var theFrame = $("#dinFrame");
(Resize = function() {
    theFrame.height($(window).height() - 100);
})();
$(window).resize(Resize);

Vad det här gör rent tekniskt är att köra en anonym funktion, som definierar en ny funktion och samtidigt kör den funktionen. Den cachar även DOM-elementet och lyssnar på resize-event, men det är inte det sexiga.

Samma kod skulle kunna skrivas:

var theFrame = $("#dinFrame");
var Resize = function() {
    theFrame.height($(window).height() - 100);
};
$(window).resize(Resize);
Resize();

Men enligt mig är det inte sexigt alls.

Samma princip används när man skriver jQuery-plugin och vill försäkra sig om att $-funktionen faktiskt pekar på jQuery och inget annat:

(function($) {

    //SAFE CLOSURE'D

})(jQuery);

För att vara extra säker kan man även lägga till window och undefined som parametrar, så ingen mixtrar med dem och förstör dina scripts. (Måste såklart köras när this pekar på det globala windowobjektet)

(function($, window, undefined) {

    //SAFER CLOSURE'D

})(jQuery, this);

Mest för att försäkra sig om att window är window och undefined verkligen är undefined.