I've created a little javascript web page for Movember to let users "shave" my beard into a moustache, or completely off.
I've done this by covering a shaved picture with a grid of divs holding the bearded picture with differing offsets. When a user moves their mouse over one of the divs, it disappears, showing the shaved skin in the picture underneath.
The problem is that in creating the 4000 little divs, the jquery.min.js script keeps popping up an "unresponsive script" error.
I'm hoping somebody can spot something really inefficient in my code, or suggest how to get Jquery to take a breath. I even tried having a "loading..." spinning wheel gif show while the code was running, but the javascript threw the hourglass so quickly that the gif never even showed up.
Here's the code:
<script type="text/javascript">
var isErase = false;
var isCover = false;
var size = 4;
var 开发者_如何转开发i = 0;
var j = 0;
$(document).ready(function(){
for (i = 0; i < 400; i += size) {
for (j = 0; j < 250; j += size) {
$('#picture').append('<div class="piece" style="background-position: -' + j + 'px -' + i + 'px;" />');
}
}
$('div.piece').click(function(event){
isCover = false;
isErase = ! isErase;
if (isErase) $('#mode').html(' *** SHAVING ***');
else $('#mode').html('OFF');
});
$('div.piece').dblclick(function(event){
isErase = false;
isCover = ! isCover;
if (isCover) $('#mode').html(' *** UNSHAVING ***');
else $('#mode').html('OFF');
});
$("div.piece").mouseenter(function(event){
if (isErase) { $(this).addClass('invisible'); }
else if (isCover) { $(this).removeClass('invisible'); }
});
});
</script>
<script type="text/javascript">
var isErase = false;
var isCover = false;
var size = 4;
var i = 0;
var j = 0;
var str = '';
$(document).ready(function(){
for (i = 0; i < 400; i += size) {
for (j = 0; j < 250; j += size) {
str += '<div class="piece" style="background-position: -' + j + 'px -' + i + 'px;" />';
}
}
$('#picture').html(str).delegate('div.piece', 'click', function (event) {
isCover = false;
isErase = ! isErase;
if (isErase) $('#mode').html(' *** SHAVING ***');
else $('#mode').html('OFF');
}).delegate('div.piece', 'dblclick', function(event) {
isErase = false;
isCover = ! isCover;
if (isCover) $('#mode').html(' *** UNSHAVING ***');
else $('#mode').html('OFF');
}).delegate('div.piece', 'mouseenter', function(event) {
if (isErase) { $(this).addClass('invisible'); }
else if (isCover) { $(this).removeClass('invisible'); }
});
});
</script>
The biggest inefficiency's in your code were as follows:
- Manipulating the DOM 100,000 times; adding a new
piece
each time. Creating a string with the HTML and adding it once is so much quicker. - Not caching your jQuery selectors. You were looking up the div with the id of 'picture' and building a jQuery object of it 100,000 times. Each time you used
$('div.piece')
, you looked up all 100,000 of those. Either chain your jQuery methods, or cache the objects. - Binding the same event to all 100,000
piece
's. Take advantage of Javascripts event bubbling, and add the event once to an ancestor of the elements (see delegate()).
This helps for me in safari, but either way I don't this is something that is really suitable for HTML and JavaScript.
<script type="text/javascript">
var isErase = false;
var isCover = false;
var size = 4;
var i = 0;
var j = 0;
$(document).ready(function(){
for (i = 0; i < 400; i += size) {
for (j = 0; j < 250; j += size) {
$('#picture').append('<div class="piece" style="background-position: -' + j + 'px -' + i + 'px;" />');
}
}
$("#picture").live('click',function(event){
isCover = false;
isErase = ! isErase;
if (isErase) $('#mode').html(' *** SHAVING ***');
else $('#mode').html('OFF');
});
$("#picture").live('dblclick',function(event){
isErase = false;
isCover = ! isCover;
if (isCover) $('#mode').html(' *** UNSHAVING ***');
else $('#mode').html('OFF');
});
$("div.piece").live('mouseenter',function(event){
if (isErase) { $(this).addClass('invisible'); }
else if (isCover) { $(this).removeClass('invisible'); }
});
});
</script>
精彩评论