Browsertests Teil 2 – Die Konsole im Internet Explorer

In meinen letzen Beitrag zu Browsertest habe ich bereits von Browserstack berichtet und welche Vorzüge die Seite bietet. Heute Möchte ich ein anders Problem ansprechen. Der geneigte Webentwickler verwendet gerne und oft die ein oder andere Konsolen-Ausgabe z.B. via “console.log()”. Nun kommt es aber  in allen Internet Explorer Version bis inklusive Version 8 zu einem […]

Quelle: Die Konsole im Internet Explorer | joocom GmbH – Ihr Community Dienstleister in Hannover

Erster Eindruck von CoffeeScript

Die Programmiersprache CoffeeScript hatte sofort nachdem ich ihren Namen gehört hatte, einen Stein bei mir im Brett. Die Idee hinter der Sprache ist, JavaScript zu vereinfachen,  zu erweitern und vor allem die Lesbarkeit zu erhöhen. Ich las immer wieder von ihr z.B. als Dropbox seinen kompletten JavaScript-Code auf CoffeeScript umgestellt hat.
Ich wollte sie unbedingt einmal selber ausprobieren. Da mir aber gerade kein passendes Projekt einfallen wollte, habe ich kurzerhand meinen Spaceshoter SpaceJs in CoffeeScript umgesetzt. Bei der gelegenheit habe ich ihn auch gleich nochmal überarbeitet.

Einrichtung

CoffeeScript lässt sich ohne Probleme mit dem Node-Paketmanager npm installieren:

sudo npm install -g coffee-script

Falls man dafür aber keine Rechte besitzt oder npm nicht zur Verfügung steht, kann die aktuelle Version auch auf einer andere Maschine kompiliert und anschließen auf den Arbeitsrechner kopiert werden. In diesem Fall empfiehlt sich ein Alias auf die Installation:

alias coffee='~/bin/coffee-script/bin/coffee'

Zum programmieren habe ich den Editor gedit verwendet. Ich habe ihn noch um das Highlighting von CoffeeScript angereichert. Die Files und Anleitung dafür findet ihr im GitHub Projek von wavded.

Das Programmieren

Am Anfang war es sehr gewöhnungsbedürftig auf sämtliche Klammern, Semikolons und vor allem auf „var“ zu verzichten. Da ich im Arbeitsalltag die Programmiersprache Java, mit ähnlicher Syntax, einsetze habe ich vielleicht noch etwas länger gebraucht. Da sie optional, trotzdem verwendet werden können ist das aber kein so großes Problem. Nach eine kurzen Eingewöhnungszeit hatte ich den Kniff auch raus.
Trotz der vorzüglichen Dokumentation,habe ich immer wieder auf den kompilierten JavaScript-Code zugegriffen. Dies hilf mir die Arbeitsweise und Syntax besser zu verstehen. Diese Vorgehen kann ich empfehlen und hat mir persönlich sehr geholfen. Ich habe festgestellt, das der kompilierte JavaScript-Code meinem selbstgeschriebenen Code ziemlich ähnlich sieht, dass liegt aber hauptsächlich daran, das ich ebenfalls stark auf Objekte mit Konstruktoren und das Prototype-Konzept setze. Den Erstellte Code wird im Browser verwende, aus diesem Grund hab ich mich dazu entschieden alles in einer JavaScript-Datei (Game.js) zu kompilieren.

coffee --output js/ --join Game.js --compile coffee/*.coffee

So sieht z.B. das Gegnerobjekt nach der Überarbeitung aus:

class Opponent

	constructor: (@$boardNode)  ->
		@dead = false
		@$opponentNode = @create()

	create: => 
		$opponentNode = $("<div class='opponent'>").appendTo(@$boardNode)
		$opponentNode.css('left', @findPosition($opponentNode) + 'px')
		return $opponentNode;

	findPosition: ($node) =>
		range = (Math.round((@$boardNode.width() - (@$boardNode.offset().left * 2)) / $node.width()) * $node.width()) * -1
		left = Math.round(Math.random() * range)
		left = @$boardNode.offset().left +  left
		if left > @$boardNode.offset().left + @$boardNode.width() - $node.width()
			return @findPosition($node)
		return left

	destroy: =>
		if @$opponentNode.length
			new Explosion(@$boardNode, @$opponentNode.offset())
			@$opponentNode.remove()
			@dead = true

	move: =>
		return if @dead
		@$opponentNode.css "margin-top", "+=1px"

Fazit

Ich schreibe für mein Leben gerne JavaScript-Code, schaue ihn mir gerne an und finde ihn in den meisten Fällen gut lesbar. Ich werde auch weiterhin bei der Arbeit „Plain-JavaScript“ verwenden. Im Arbeitsalltag kann man in meiner Umfeld leider nicht davon ausgehen, das CoffeeScript auf eine breite Akzeptanz stößt, denn die Syntax ist erst auf den 2. Blick wirklich verständlich. Dabei spielen vor allem die verdrehten Conditions und die Tatsache das „->“ und „=>“ leicht zu vertauschen sind, eine große Rolle.

CoffeeScript erfordert, im Vergleich zu anderen Programmiersprachen, einen sehr geringen Einarbeitungsaufwand. Bei guten Vorkenntnissen in JavaScript hat man nach nur ein paar Stunden das meiste drauf.

Durch das betrachten des echt guten, generierten JavaScript-Codes konnte ich meinen eigenen JavaScript-Code verbessern. Einige Syntax-Varianten fand ich so gut, das ich sie übernommen haben. Als Beispiel möchte ich hier „_this“ als Name für die zwischengespeicherte „this“ Variable nennen.
Falls es sich anbietet werde ich in privaten Projekte auf jeden Fall das ein oder andere mal CoffeeScript verwenden.

Das gesamte Projekt könnt ihr euch bei GitHub angucken, testen, runterladen und spielen. Viel Spaß!

[Snippet/Javascript] jquery.toggleAttr

In jQuery gibt es die Funktionen „toggle“ um Elemente ein/auszublenden und „toggleClass“ um zwischen 2 CSS-Klassen hin und her zu wechseln. Da liegt es doch nahe diese Funktion auch für sämtliche HTML-Attribute umzusetzen. Ich brauchte diese Funktion um komfortabel via Button ein Passwortfeld zu einem normalen Feld zu switchen.

(function ( $ ) {
	$.fn.toggleAttr = function(attr, value) {
		if (!this.data('old-attr')) this.data('old-attr', this.attr(attr));
		if (!this.data('new-attr')) this.data('new-attr', value);
		if (!this.data('toggle-attr')) this.data('toggle-attr', attr);
		if (this.attr(attr) == value) this.attr(attr, this.data('old-attr'));
		else this.attr(attr, value);
		return this;
	};
}( jQuery ));

Anwendungsbeispiel:

<input placeholder="Passwort" type="password" />
<button type="button" onclick="$(this).prev('input').toggleAttr('type','text')">
  show/hide pass
</button>

Den Code findet ihr auch auf GitHub.

SpaceJS

spaceJS_screenshotAufbauend auf dem Code vom letzen Post habe ich ein zweites JavaScript Spiel erstellt.
Dieses macht ein bisschen mehr her war aber immernoch relativ leicht umzusetzen. Ich habe noch ein bisschen das Problem einen guten Fluss von Bewegung und Zeitintervall hin zu bekommen deswegen kann es ab und zu ein bisschen ruckeln.
Diesmal wird die Maus verwendet um die Spielfigur (Raumschiff) zu steuern. Für die Umsetzung habe ich nur 2 zusätzliche JavaScript Objekte benötigt, eins für die Kugeln (Bullet.js) und eins für die Explosion (Explosion.js). Bei einigen Positionsangaben war ich zu faul sie dynamisch zu ermitteln, also immer wenn ihr eine „Magic Number“ im Code entdeckt ist es ein Abstand von oben/unten/links. 😉 Da der Code dem Ablauf und Aufbau des anderen Spiels gleicht, verzichte ich hier mal auf eine Erklärung, falls ihr aber noch ein paar Fragen habt immer raus damit.

Wie immer dürft ihr das Spiel gerne runterladen und verändern und erweitern. Ich würde mich auch freuen wenn ihr mir ein Kommentar schreibt wie viele Abschüsse ihr gelandet und welches Level ihr erreicht habt.

spielen / runterladen

Objektorientiertes JavaScript Spiel

Dieses kleine Spiel soll mit einfachen Mittel zeigen das man auch in JavaScript gutlesbaren, objektorientierten Code schreiben kann.

Als Unterstützung verwende ich die jQuery Bibilothekt um mir das arbeiten mit dem DOM und das Positionieren zu erleichtern.

Für das ganze Spiel werden nur 4 Objekte benötigt, wobei eines nur ein Containerobjekt für Tastencode-Zuweisungen ist. Ich habe für alle Objekte eine eigene Datei erstellt um eine gute Lesbarkeit zu erreichen. Immer wenn die Datei ein JavaScript-Objekt repräsentiert schreibe ich den Dateiname groß.

Unser Startpunkt ist die Datei Game.js. Als Erstes wird für jedes Element mit der Klasse „.game“ eine Game-Objekt erzeugt, dadurch werden auch die im Konstruktor definierten Variablen zugewiesen und Methoden ausgeführt. Ich initialisiere einen KeyListener um auf Tastatureingaben zu reagieren. In der Datei „util.js“ ist das Containerobjekt mit den Tastencode-Zuweisungen.

In der Methode „run()“ wird das Spiel gestartet. Mit der setInerval Funktion des Window-Objekts erreichen wir eine dauerhafte Ausführung unseres Spiels. In jedem Durchlauf können wir die Hindernisse bewegen, Kollision zwischen Spieler und Gegenständen feststellen und gegeben Falls darauf reagieren.

Im Konstuktor des Game-Objektes wird außerdem für jedes Hinderniss und den Spieler jeweils Objekte erstellt. Diese sind dem Game-Objekt bekannt um in der „run()“
Methode mit ihnen zu arbeiten.

$(document).ready(function () {

    $('.game').each(function () {
        new Game($(this));
    });

});

function Game($game) {
    this.$board = $game.find('.board');
    this.speed = 10;
    this.pause = false;
    this.gameOver = false;
    this.player = new Player(this.$board);
    this.$text = $game.find(".text");
    this.barriers = this.initBarrier();
    this.initKeyListener();
    this.run();
}

Game.prototype.run = function () {
    var game = this;
    var $player = game.player.$player;

    window.setInterval(function () {

        if (game.gameOver || game.pause) {
            return;
        }

        $.each(game.barriers, function (i) {

            this.move();

            if ($player.collidesWith(this.$barrier).length) {
                game.gameOver = true;
                game.$text.text("GAME OVER!");
            }

            if ($player.offset().top > (game.$board.height() + $player.height())) {
                game.pause = true;
                game.$text.text("WIN!");
            }

        });

    }, game.speed);
};

Game.prototype.initKeyListener = function () {
    var game = this;
    $(window).bind('keydown', function (e) {
        switch (e.keyCode) {

            // Reset
        case Keys.ESC:
            game.player.reset();
            game.pause = false;
            game.gameOver = false;
            game.$text.text("");
            break;

            // Pause
        case Keys.SPACE:
            game.pause = !game.pause;
            break;

            // Spieler zurücksetzen
        case Keys.POS1:
            if (!game.pause && !game.gameOver) game.player.reset();
            break;

        case Keys.LEFT:
            if (!game.pause && !game.gameOver) game.player.moveLeft();
            break;

        case Keys.UP:
            if (!game.pause && !game.gameOver) game.player.moveUp();
            break;

        case Keys.RIGHT:
            if (!game.pause && !game.gameOver) game.player.moveRight();
            break;

        case Keys.DOWN:
            if (!game.pause && !game.gameOver) game.player.moveDown();
            break;

        default:
            break;
        }
    });
};

Game.prototype.initBarrier = function () {
    var $board = this.$board;
    var barriers = [];
    $board.find('.barrier').each(function () {
        barriers.push(new Barrier($board, $(this)));
    });
    return barriers;
};

Das Player-Objekt wird lediglich vom KeyListener angesprochen. Je nach Tastatureingabe wird das enthaltene HTML bzw. jQuery Objekt bewegt.

function Player($board) {
    var p = this;
    p.speed = 5;
    p.$board = $board;
    p.$player = p.$board.find('.player:eq(0)');
}

Player.prototype.reset = function () {
    this.$player.css("margin", "0");
};

Player.prototype.moveUp = function () {
    var p = this;
    if (p.$player.offset().top > p.$board.offset().top) {
        p.$player.css("margin-top", "-=" + p.speed + "px");
    }
};

Player.prototype.moveDown = function () {
    var p = this;
    if (p.$player.offset().top < (p.$board.height() + p.$player.height())) {
        p.$player.css("margin-top", "+=" + p.speed + "px");
    }
};

Player.prototype.moveLeft = function () {
    var p = this;
    if (p.$player.offset().left > p.$board.offset().left) {
        p.$player.css("margin-left", "-=" + p.speed + "px");
    }
};

Player.prototype.moveRight = function () {
    var p = this;
    if (p.$player.offset().left - p.speed < p.$board.width()) {
        p.$player.css("margin-left", "+=" + p.speed + "px");
    }
};

Die „move()“ Methode des Barrier Objektes wird in jedem Durchlauf der „run()“ Methode aufgerufen, dadurch bewegt es sich automatisch und wird so „animiert“.

function Barrier($board, $barrier) {
    var b = this;
    b.$board = $board;
    b.$barrier = $barrier;
    b.moveLeft = false;
    b.sign = "+";
    b.range = 1;
}

Barrier.prototype.move = function () {
    var b = this;

    // Move left
    if (b.nachLinks || (b.$barrier.offset().left + b.$board.position().left >= b.$board.width())) {
        b.sign = "-";
        b.nachLinks = true;
    }

    // Move right
    if (b.$barrier.offset().left <= b.$board.offset().left) {
        b.sign = "+";
        b.nachLinks = false;
    }

    b.$barrier.css("margin-left", b.sign + "=" + b.range + "px");

};

Hier könnt ihr meine Sourcen Downloaden um sie euch genauer anzusehen und vielleicht selber noch ein bisschen damit rum zu spielen.

Download

[Snippet/Javascript] Div mitscrollen

Hier mal wieder ein Mini Snippet zu JavaScript. Mit jQuery ist es ziemlich einfach Scroll-Eigenschaften

auf ein anderes Element zu übertragen. Wenn also das obere Div gescrollt wird, scrollt das untere

automatisch mit. Anbei der Quelltext und hier eine Livedemo.

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
   <head>
      <title>test</title>
      <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
      <style type='text/css'>
         div { font-size:200px; width:200px; height:100px; white-space:nowrap; overflow:scroll }
         div + div { overflow:hidden }
      </style>
   </head>
   <body>
      <div id="div1">test test test test test test</div>
      <div id="div2">test test test test test test</div>
      <script type="text/javascript">
         $('#div1').scroll(function() {
         $('#div2').scrollLeft(this.scrollLeft);
         $('#div2').scrollTop(this.scrollTop);
         });
      </script>
   </body>
</html>

[Snippet/Javascript] divAusblenden – Animation

Hier ein kleines Beispiel wie „Animation“ ohne ein Javascript Framework realisieren werden kann.
In diesem Fall wird ein Div nach 3 Sekunden immer kleiner und dann ausgeblendet.
Das ganze passiert rekursiv.

 

// Meldung nach 3 Sekunden wieder ausblenden
var meldung = document.getElementById("meldung");
if (meldung) {
    window.setTimeout(function () {
        divAusblenden("meldung", meldung.offsetWidth - 10, meldung.offsetHeight - 10);
    }, 3000);
}
// Meldungen langsam kleiner werden lassen und dann ausblenden

function divAusblenden(id, breite, hoehe) {
    var reentrant = 0;
    if (!reentrant) {
        reentrant = 1;
        var move = 0,
            element = document.getElementById(id);
        if (hoehe > 0) {
            hoehe -= 1;
            move++;
        }
        if (breite > 0) {
            breite -= 1;
            move++;
        }
        element.style.visibility = (breite > 0 && hoehe > 0) ? 'visible' : 'hidden';
        if (move) {
            element.style.height = hoehe + 'px';
            element.style.width = breite + 'px';
            if (hoehe >= 1 && hoehe <= 12) {
                element.style.fontSize = hoehe - 1 + 'px';
            }
            window.setTimeout(function () {
                divAusblenden(id, breite, hoehe);
            }, 30);
        }
        reentrant = 0;
    }
}

[Snippet/Javascript] sendeAnfrage – Ajax leicht gemacht

Hier ist eine Beispielhafte, minimale Implementierung des HTTPRequestObjects.
Die Funktion kann schnell eingebunden werden um Inhalte nachzuladen oder
allgemein Ajax zu ermöglichen. Zusammen mit meiner Funktion sammleDaten()
steht dem Eisatz von Ajax nichts mehr im Weg und hat sich in Zahlreichen Projekten
bewert.

/**
 *
 * Sendet eine Ajax-Anfrage an einen Server
 *
 * @param methode String POST oder GET / Wird etwas anderes übergeben wird GET verwendet
 * @param zielUrl String An Was die Anfrage geschickt werden soll (z.B. ein Skript auf dem Server)
 * @param verarbeiter CallBack Function JS-Funktion die die Rückgabe
 * des Servers verarbeitet und die Rückgabe als Parameter bekommt
 * (optional)
 * @param parameter String / Array Parameter die an den Server geschickt
 * werden sollen. Entweder einer als String Bsp.: "kekse=true" oder
 * mehrere als Array Bsp.: ["kekse=true","kuchen=false"]
 * (optional)
 * @return Rückgabe des verarbeiters, falls kein Verarbeiter gesetzt ist true
 * bei Erfolg und false bei Fehlschlag
 *
 */

function sendeAnfrage(methode, zielUrl, parameter, verarbeiter) {
    if (parameter && parameter.constructor == Function) {
        verarbeiter = parameter;
        parameter = undefined;
    }

    methode = methode.toUpperCase();
    if (methode != "POST" && methode != "GET") methode = "GET";

    var request = null;
    if (window.XMLHttpRequest) request = new window.XMLHttpRequest();
    if (window.ActiveXObject) request = new window.ActiveXObject("Microsoft.XMLHTTP");
    if (request == null) return false;

    request.onreadystatechange = function () {
        if (request.readyState == 4) {
            var rueckgabe = request.responseText;
            if (request.status == 200) {
                if (verarbeiter != null) return verarbeiter(rueckgabe);
                return true;
            }
            return false;
        }
    }

    var uebertraeger = null;
    if (parameter !== undefined) {
        if (parameter.constructor == Array) {
            for (var i in parameter) {
                if (i > 0) uebertraeger = uebertraeger + parameter[i] + "&";
                else uebertraeger = parameter[i] + "&";
            }
        } else uebertraeger = parameter;
    }

    if (methode == "GET" && uebertraeger != null) zielUrl = zielUrl + "?" + uebertraeger;
    request.open(methode, zielUrl, true);
    if (methode == "POST") request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    request.send(uebertraeger);

}

Beispiel für die Verwendung:

 

// Initialisieren
var initialisirenFunktionen = [];
onload = function () {
    for (var i in initialisirenFunktionen) initialisirenFunktionen[i]();
}

// Erstelle LoadBereich
initialisirenFunktionen.push(function () {
    var loadContent = document.createElement("DIV");
    loadContent.id = "loadContent";
    document.body.appendChild(loadContent);
});

// Reichere AjaxButton um Anfrage-Event an
initialisirenFunktionen.push(function () {
    var sendenButton = document.getElementById("senden");
    sendenButton.onclick = function () {
        sendeAnfrage("POST", "MeinServlet", sammleDaten(document.meinForm), schreibeResponseInLoadBox);
    };
});

// CallbackFunktion für die Ajax-Anfrage (Ergebniss verarbeiten)

function schreibeResponseInLoadBox(r) {
    var loadContent = document.getElementById("loadContent");
    loadContent.innerHTML = r;
}

[Snippet/Javascript] sammleDaten

Wenn man ein Formular mit Ajax abschicken will kommt man schnell in die Situation,
dass man sich die Daten oder einen Datensatz selbst zusammenbauen muss.
Mit jQuery ist das kein Problem, ohne sieht die Sache da aber schon ganz anders aus.

Meine Funktion geht Rekursive das übergebene HTMLElement durch und guckt ob es ein
Typisches Formular Element ist (Textarea, Input und Selextbox). Ist das der Fall wird der
Value mit dem Namen in einem Array gespeichert und am Ende zurückgegeben.

/**
 *
 * Liefert alle Daten aus einem Formular in einem Array
 *
 * @param formular HTMLElement Formular in dem nach Daten gesucht werden soll
 * @return Array Im Format ["key=value","key=value"]
 *
 */

function sammleDaten(formular) {
    var daten = [];
    var ue = formular.childNodes;
    for (var i in ue) {
        if (ue[i].tagName && ue[i].name &&
            (ue[i].tagName == "INPUT" || ue[i].tagName == "TEXTAREA" || ue[i].tagName == "SELECT")) {
            var inhalt = null;
            if (ue[i].value) {
                inhalt = ue[i].value;
            }
            daten.push(ue[i].name + "=" + inhalt);
        }
        daten = daten.concat(sammleDaten(ue[i]));
    }
    return daten;
}

[Snippet/Javascript] toggleClass ohne jQuery

Ich habe vor kurzem eine Javascript Funktion gebracht um schnell CSS KLassen zu Tauschen. In jQuery gibt es dafür die Funktion „toggleClass“. Leider stand mir in dieser Situation das Framework nicht zur Verfügung also habe ich mir die Funktion kurzerhand selber geschrieben.

Das Problem bei der Sache ist das man nicht einfach den Attribut Classname ersetzen darf da das HTML-Element vielleicht noch andere Klassen besitzt. In meiner Funktion können entweder zwei oder eine CSS-Klasse übergeben werden.
Bei zwei werden sie, falls vorhanden, getauscht, wenn noch keine von beiden vorhanden ist, wird die erste angefügt.
Bei dem Fall das nur eine Klasse übergeben wurde, wird zuerst geguckt ob die diese schon gesetzt ist. Falls ja wird sie mit nichts ersetzt andernfalls wird sie angefügt. Meine Funtion müsste sich also genauso wie das jQuery Pendant verhalten.

/**
 *
 * Wechselt die CSS-Klasse eines übergebenen Elements
 *
 * @param HTMLElement Element dessen Klasse gewechselt wird
 * @param cls1 Klasse die gesetzt oder weg gemacht wird
 * @param cls2 Klasse mit der getauscht wird. Falls dieser Parameter
 *             nicht gesetzt ist wird cls1 durch nichts ersetzt
 */

function toggleClass(elm, cls1, cls2) {
    if (!cls2) cls2 = "";
    elm.ersetzeKlasse = function (haystack, needle) {
        var re = new RegExp(haystack, "g");
        this.className = this.className.replace(haystack, needle);
    }

    if (elm.className.indexOf(cls1) > -1) elm.ersetzeKlasse(cls1, cls2);
    else if (elm.className.indexOf(cls2) > -1) elm.ersetzeKlasse(cls2, cls1);
    else elm.className += " " + cls1;
}