I created a basic jQuery plugin to make a basic sliding tile puzzle. /* * tilePuzzle.src.js * * A simple tile puzzle jQuery plugin by @matthewfedak * *//*jslint browser: true*//*global $, jQuery, alert*/(function ($) {use strict;$.fn.tilePuzzle = function (options) {    var self = $(this);    var tilePuzzle = null;    var defaults = {        keyAccess: false,        tileContainerClass: 'tiles',        level: 4,        maxWidth: 600    };    var construct = {        container : null,        imageDimensions : {},        tileDimensions : {},        spareTile: null,        init : function () {            var _this = this;            _this.container = $('<ul/>').addClass(tilePuzzle.tileContainerClass);            self.append(_this.container);            if (tilePuzzle.level < 3) {                tilePuzzle.level = 3;            }            _this.loadRemoteImage();            _this.container.on('click', 'li', function () {                _this.moveTile($(this), $(this).css('top'), $(this).css('left'));            });            return _this;        },        loadRemoteImage : function () {            var _this = this;            var img = new Image();            img.onload = function () {                _this.imageDimensions = { 'width': this.width, 'height': this.height };                if (_this.imageDimensions.width > _this.maxWidth) {                    var newHeight = (_this.maxWidth / _this.imageDimensions.width) * this.height;                    _this.imageDimensions.width = _this.maxWidth;                    _this.imageDimensions.height = newHeight;                }                _this.createTiles();            };            img.src = tilePuzzle.imageUrl;            return _this;        },        createTiles : function () {            var _this = this;            var x, n, t = null;            _this.tileDimensions = {                'width': Math.abs(_this.imageDimensions.width / tilePuzzle.level),                'height': Math.abs(_this.imageDimensions.height / tilePuzzle.level)            };            for (x = 0, n = 0, t = 0; t < tilePuzzle.level * tilePuzzle.level; t++) {                _this.container.append(                    $('<li/>').css({                        'width': _this.tileDimensions.width,                        'height': _this.tileDimensions.height,                        'background-image': 'url(' + tilePuzzle.imageUrl + ')',                        'background-position': '-' + (t * this.tileDimensions.width) + 'px -' + (x * _this.tileDimensions.height) + 'px',                        'background-size': _this.imageDimensions.width + 'px ' + _this.imageDimensions.height + 'px',                        'top': (x * _this.tileDimensions.height) + 'px',                        'left': (n * _this.tileDimensions.width) + 'px'                    })                );                n++;                if (((t + 1) % tilePuzzle.level) === 0) {                    x++;                    n = 0;                }            }            _this.container                .children(':last-child')                .addClass('spare');            _this.spareTile = _this.container.find('.spare');            self.css({                'width': _this.imageDimensions.width,                'height': _this.imageDimensions.height            }).fadeIn();            _this.scrambleTiles(tilePuzzle.level);            return _this;        },        scrambleTiles : function (limit) {            var _this, i, tile1, tile2, tile1Position, timer;            _this = this;            limit = limit * limit;            i = 0;            setTimeout(function () {                timer = setInterval(function () {                    tile1 = _this.container.children().eq(Math.floor((Math.random() * (tilePuzzle.level * tilePuzzle.level)) + 1));                    tile2 = _this.container.children().eq(Math.floor((Math.random() * (tilePuzzle.level * tilePuzzle.level)) + 1));                    tile1Position = { 'top': tile1.css('top'), 'left': tile1.css('left') };                    tile1.css({                        'top': tile2.css('top'),                        'left': tile2.css('left')                    });                    tile2.css({                        'top': tile1Position.top,                        'left': tile1Position.left                    });                    i++;                    if (i > limit) {                        clearInterval(timer);                    }                }, 50);            }, 1000);        },        moveTile: function (tile, top, left) {            var _this, topDistance, leftDistance;            _this = this;            topDistance = Math.abs(parseInt(top, 10) - parseInt(_this.spareTile.css('top'), 10));            leftDistance = Math.abs(parseInt(left, 10) - parseInt(_this.spareTile.css('left'), 10));            if ((leftDistance + topDistance) <= Math.round(_this.tileDimensions.width) || (leftDistance + topDistance) <= Math.round(_this.tileDimensions.height)) {                tile.css({                    'top': _this.spareTile.css('top'),                    'left': _this.spareTile.css('left')                });                _this.spareTile.css({                    'left': left,                    'top': top                });            }        }    };    tilePuzzle = $.extend(construct, defaults, options);    tilePuzzle.init();    return self;};}(jQuery));Links to demo is on my github page.tilePuzzle is available on GitHubAny sort of feedback is welcome.
jQuery Plugin - Sliding Tile Puzzle
javascript;jquery;plugin;sliding tile puzzle
The first thing I've noticed was this line:(function ($) {And I though to myself: No way!Then I read at the end, and I saw this:}(jQuery));In the name of all Javascript developers, I thank you for taking this very simple step! You have no idea how much is helps us and helps you!Inside your function, you have this:var self = $(this);Please, don't do that! Don't set anything on the self variable! The self points to the window object. You can use it by itself or using the dot notation. That variable is the only way to access the current window on a WebWorker. Please, leave it unset. Use $parent or something.On your init, you have this:_this.container.on('click', 'li', function () {    _this.moveTile($(this), $(this).css('top'), $(this).css('left'));});You are calling jQuery just so that you can pass an instance to move the tile somewhere? You know, you would be better off without jQuery inside that handler. Like this:_this.container.on('click', 'li', function () {    _this.moveTile(this, this.style.top, this.style.left);});See? So much simpler!!! You can change your moveTile into this:    moveTile: function (tile, top, left) {        var _this = this;        var topDistance = Math.abs(parseInt(top, 10) - parseInt(_this.spareTile.style.top, 10));        var leftDistance = Math.abs(parseInt(left, 10) - parseInt(_this.spareTile.style.left, 10));        if ((leftDistance + topDistance) <= Math.round(_this.tileDimensions.width)            || (leftDistance + topDistance) <= Math.round(_this.tileDimensions.height)) {            tile.style.top = _this.spareTile.style.top;            tile.style.left = _this.spareTile.style.left;            _this.spareTile.style.top = top;            _this.spareTile.style.left = left;        }    }Just make sure that _this.spareTile is an Element and not a jQuery instance. For that, on your createTiles, you have this:_this.container    .children(':last-child')    .addClass('spare');_this.spareTile = _this.container.find('.spare');Which can be changed to:_this.spareTile = _this.container    .children(':last-child')    .addClass('spare')[0];If you want to keep using jQuery, you can change the handler to:_this.container.on('click', 'li', function () {    var $this = $(this);    _this.moveTile($this, $this.css('top'), $this.css('left'));});Remember that event handlers must do what they have to do as quick as possible! If you can shave off 1ms, shave it!