Почему после использования data атрибута в setTimeout, он перестаёт быть доступным ?
Добрый день.
Есть загвоздка, и я не могу понять до конца, из-за чего она возникает, поэтому попробую описать проблему, по возможности, сжато но подробно. я новичок, поэтому не судите строго за возможный каламбур... Вообщем, я делаю карточную игруху(посередине игровое поле из 9 ячеек. 3 по горизонту 3 по вертикали. От игрового поля слева находятся ваши карты, а справа карты компа. У каждой карты есть 4 цифры слева, справа, сверху и снизу. По этим цифрам происходит сравнение между картами.) Ход игрока реализован при помощи jquery, draggable и droppable. Ход компа осуществляется в droppable свойстве drop: при помощи append. Вся логика написана и всё работает. И собственно теперь к проблеме, сейчас хочу сделать чтобы действие append после перетаскивания происходило с 2 сек задержкой. Написал код, и append работает как задумывалось, НО потом вся логика рушиться, т.к. элемент, который я передаю append в дальнейшем везде перестаёт быть доступным (выдаёт undefined). Код выглядит вот так: // перемешиваю колоду. // var arr = [0,1,2,3,4]; function shuffle(o){ for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); return o; } shuffle(arr); var cards = new Array(); for (var i=0; i<5; i++) { cards[i] = $('.ssComp:eq('+(arr[i])+')'); } // ------------------------------------- // $('.game-field').droppable({ activeClass: 'active-th', hoverClass: 'hover-th', drop: function(event, ui) { $card = $(ui.draggable); $(this).prepend($card); var self = $(this); var appendCard = function(arg) { var y = $(arg).append(cards.shift()).find('img').css({ 'border': '10px solid #FF0000', 'width': '150px', 'height': '150px' }).animate({ 'width': '100%', 'height': '100%' }); var q = $(arg).droppable('disable'); return y, q; } setTimeout(function(){appendCard.call(self, '[data-id='+(self.data('id')+1)+']')}, 2000); У всех ячеек игрового поля есть data-id. По дата атрибуту собственно и происходит везде обращение. Функция appendCard вставляет карту компа справа от той ячейки, в которую user перетащил свою карту. (логику проверок на вставку в поле я тут опустил, т.к. это не важно сейчас). В дальнейшем по игровой логике, вставленную компом карту, нужно будет сравнивать с соседними картами по всем сторонам (справа, снизу, слева, сверху). Обращение к этим ячейкам выглядит вот так: var insertComp = $('[data-id='+($(this).data('id')+1)+'] .ssComp'); var topSs = $('[data-id='+($(this).data('id')-2)+'] .ss') var topComp = $('[data-id='+($(this).data('id')-2)+'] .ssComp'); var bottomComp = $('[data-id='+($(this).data('id')+4)+'] .ssComp'); var bottomSs = $('[data-id='+($(this).data('id')+4)+'] .ss'); var rightSs = $('[data-id='+($(this).data('id')+2)+'] .ss'); var rightComp = $('[data-id='+($(this).data('id')+2)+'] .ssComp'); var leftSs = $('[data-id='+($(this).data('id')+0)+'] .ss'); var leftComp = $('[data-id='+($(this).data('id')+0)+'] .ssComp'); ss - класс колоды юзера, ssComp - колоды компа. (разметку страницы если нужно могу выложить) у них так же есть data-user=1 для .ss и data-user=2 для .ssComp Дальше сравниваю карту: setTimeout(function(){leftInsert()}, 500); // с той, что СЛЕВА от неё // setTimeout(function(){topInsert()}, 500); // с той, что СВЕРХУ над ней // setTimeout(function(){rightInsert()}, 500); // с той, что СПРАВА от неё // setTimeout(function(){bottomInsert()}, 500); // с той, что СНИЗУ под ней // приведу пример одной из функций, т.к. они все подобны. // функция сравнение ответной карты с той, что снизу под ней // function bottomInsert () { var x; if (insertComp.data('user') != bottomComp.data('user') || insertComp.data('user') != bottomSs.data('user')) { if (insertComp.data('bottom') > bottomComp.data('top') || insertComp.data('bottom') > bottomSs.data('top')) { bottomComp.data('user', 2); bottomSs.data('user', 2);undefined if (bottomSs.data('user') === 1) { x = green(bottomSs); u2++; } else if (bottomSs.data('user') === 2) { x = red(bottomSs); u2 +=2; u1--; } if (bottomComp.data('user') === 1) { x = green(bottomComp); u2++; } else if (bottomComp.data('user') === 2) { x = red(bottomComp); u2 +=2; u1--; } } } return x, u1, u2; } Извиняюсь, что кучу всего приплёл, но теперь ПЕРЕХОДИМ К САМОМУ ИНТЕРЕСНОМУ. В принципе сама проблема представлена ниже, а всё что выше, для понимания из-за чего это может возникать. И так, если я пишу вот так: appendCard('[data-id='+($(this).data('id')+1)+']'); То всё прекрасно работает. Крата вставляется справа от перетаскиваемой, производятся все логические проверки на сравнения с другими картами и т.д. Правда, конечно, за исключением того, что карта вставляется сразу же без задержки. Если тут вызвать: alert($('[data-id='+($(this).data('id')+1)+'] .ssComp').data('user')); // возвращает 2. как и положено Ну а мне как раз нужно сделать паузу в этом моменте. Поэтому я пишу так: setTimeout(function(){appendCard.call(self, '[data-id='+(self.data('id')+1)+']')}, 1000); Здесь карта вставляется куда нужно, но логика проверок отваливается. Если вызвать: alert($('[data-id='+($(this).data('id')+1)+'] .ssComp').data('user')); // возвращает undefined. хотя если наполнить игровые поля картами и например тут вызвать так: alert($('[data-id='+($(this).data('id')+4)+'] .ssComp').data('user')); // вернёт 2 как и положено (или 1 в зависимости от карты и какой класс указать ss или ssComp). Главное что this работает корректно. alert($('[data-id='+($(this).data('id')+1)+']').data('id')); // так же возвращает верное значение. ( в зависимости куда перту перетащить) хотя с таймером: setTimeout(function(){alert($('[data-id='+($(this).data('id')+1)+']').data('id'))}, 1500); // возвращает undefined. :blink: Чего-то я явно не понимаю. Но что-то явно загадочное происходит в следовании друг за другом этих методов setTimeout. |
tomberty,
потому что когда setTimeout запустит функцию this будет window |
Хорошо, но ведь я же указал контекст для this:
var self = $(this); {appendCard.call(self, . . . } |
tomberty,
не очень понятно ... вы что хотите карту в карту вставить? |
карта вставляется в ячейку справа, если это ячейка пустая, от той карты, которую перетащили. Если ячейка занята, то будет вставляться под ней, затем слева или сверху. по наличию свободных мест. Но это я опустил, т.к. там всё одно и тоже везде.
Вправо я её вставляю с задержкой вот так: setTimeout(function(){appendCard.call(self, '[data-id='+(self.data('id')+1)+']')}, 2000); на данном этапе сама вставка работает правильно, но теперь: alert($('[data-id='+($(this).data('id')+1)+'] .ssComp').data('user')); // возвращает undefined. у этой карты (ssComp), есть ещё другие дата атрибуты помимо юзера и ко всем не достучаться. как будто он не видет в ней карту (ssComp). Хотя если смотреть через firebug, то видно что элемент ssComp там точно находиться. Например если просто сейчас просто обратиться к ячейке по data-id то всё Ок: alert($('[data-id='+($(this).data('id')+1)+']').data('id')); Но именно карты он не видит. И ещё раз подчеркну, что если писать вставку без таймаута: appendCard.call(self, '[data-id='+(self.data('id')+1)+']'); То: alert($('[data-id='+($(this).data('id')+1)+'] .ssComp').data('user')); работает корректно. Что-то этот setTimeout явно делает не понятные мне вещи.... |
tomberty,
не пишите this |
после вставки карты, даже если я обращусь к конкретной ячейке (заранее понимая куда комп вставит карту ответным ходом), например вот так:
alert($('[data-id='+4+'].ssComp').data('user')); Всё равно - undefined. однако, если заполнить игровое поля картами и обратиться допустим к другой ячейке, то всё Ок: alert($('[data-id='+5+'].ssComp').data('user')); Рони, спасибо что пытаешься помочь. |
tomberty,
задержка то зачем нужна? |
Я вот тут экспериментирую, и заметил одну ОЧЕНЬ странную вещь. После которой мне теперь кажется что вся проблема с очерёдностью обработки...
Сейчас, коротко для понимания, объясню принцип игровой логики. У каждой карты есть 4 цифры (от 1 до 10 ) по бокам (сверху снизу справа слева). Когда карта вставляется, она сравнивает 4 карты вокруг себя. То есть, например, вы поставили в игровое поле карту, у которой СПРАВА число 8, соответственно, она побьёт карту СПРАВА от себя, если у той число СЛЕВА будет меньше 8 (от 1 до 7 ). и так для всех сторон. Так вот, тут комп вставляет карту и потом она сравнивается по сторонам. В этом примере приведу только сравнение с левой картой (то есть с той, что перетащил юзер): setTimeout(function(){appendCard.call(self, '[data-id='+(self.data('id')+1)+']')}, 2000); // комп вставляет карту справа var leftSs = $('[data-id='+$(this).data('id')+'] .ss'); // карта слева от вставляемой setTimeout(function(){leftInsert()}, 1000); // функция leftInsert сравнивает левую цифру вставляемой карты, с правой цифрой карты, слева от вставляемой (нде .... =)) ) сейчас пример не работает, но стоит добавить alert, и Главное нажать на всплывающем окне ОК в промежутке после 2000мс но не более 3000мс, то проверка leftInsert СРАБАТЫВАЕТ, и если условия соответствующие (то есть левая цифра больше правой), то карта слева от вставляемой бьётся: setTimeout(function(){appendCard.call(self, '[data-id='+(self.data('id')+1)+']')}, 2000); // комп вставляет карту справа alert($('[data-id='+(self.data('id')+1)+'] .ssComp').data('user')); var leftSs = $('[data-id='+$(this).data('id')+'] .ss'); // карта слева от вставляемой setTimeout(function(){leftInsert()}, 1000); // функция leftInsert сравнивает левую цифру вставляемой карты, с правой цифрой карты, слева от вставляемой (нде .... =)) ) И да, если это будет любой другой alert, например такой: alert('dfgdsfghs'); то эта штука уже не прокатывает... Короче полтергейст какой-то блин!!! Вот сама функция leftInsert: var insertComp = $('[data-id='+($(this).data('id')+1)+'] .ssComp'); function leftInsert () { var x; if (insertComp != 0 || insertComp != 3 || insertComp != 6) { if (insertComp.data('user') != leftComp.data('user') || insertComp.data('user') != leftSs.data('user')) { if (insertComp.data('left') > leftComp.data('right') || insertComp.data('left') > leftSs.data('right')) { leftComp.data('user', 2); leftSs.data('user', 2); if (leftSs.data('user') === 1) { x = green(leftSs); u2++; } else if (leftSs.data('user') === 2) { x = red(leftSs); u2 +=2; u1--; } if (leftComp.data('user') === 1) { x = green(leftComp); u2++; } else if (leftComp.data('user') === 2) { x = red(leftComp); u2 +=2; u1--; } } else { return u2++; } } } return x, u1, u2; } |
задержка, просто для красоты, а то не успел Юзер ещё свою карту перетащить, как комп сразу же ходит, практически одновременно.
|
Цитата:
Цитата:
|
:) Еслиб я мог локализовать проблему, я бы это сразу сделал и не писал таки вот мемуары =)).
Проблема возникает после использования сетТаймаута, причём сам метод делает свою работу верно(то есть this тут непричём получается), НО в дальнейшем карта, которая помещается в ячейку, которая фигурировала в сетТаймауте в append становиться недоступна. |
Цитата:
карты User перетащить слева на синее поле и подождать. вопрос чего не работает - то? правьте как у вас на самом деле макет -- но не расширяйте без нужды, чтоб не утонуть в коде. <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>demo</title> <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script> <style type='text/css'>.wrapper{margin:20px auto} #User,#Comp,.game-field{border:solid 1px #555555;margin:10px;padding:20px;-moz-border-radius:5px;-webkit-border-radius:5px;float:left;height:335px;min-width:100px} .ssComp,.ssUser{cursor:pointer;border:solid 1px #333333;font-weight:bold;margin:10px 0 5px 0;padding:10px;text-align:center;background:#eee;-moz-border-radius:3px;-webkit-border-radius:3px} .game-field{padding:0px;width:335px;height:335px;background:#4169E1} .ssComp{background:#EE82EE} .game-field div{width:40px;height:40px;position:absolute} p{margin:0;padding:0} .game-field div.zip{margin:50px;padding:5px;width:160px;height:160px;background:#0FF} </style> <script type='text/javascript'> $(window).load(function () { $("#User .ssUser").draggable({ helper: "clone", appendTo: "#trash" }); function shuffle(o){ for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); return o; } var cards = shuffle($.makeArray($('.ssComp'))); $("#Comp").append(cards) $('.game-field').droppable({ activeClass: 'active-th', hoverClass: 'hover-th', drop: function(event, ui) { $card = $(ui.draggable); $(this).prepend($card); var self = $(this); var appendCard = function(arg) { var y = $(arg).append(cards.shift()).find('img').css({ 'border': '10px solid #FF0000', 'width': '150px', 'height': '150px' }).animate({ 'width': '100%', 'height': '100%' }); var q = $(arg).droppable('disable'); return y, q; //кому столь странный return ? } setTimeout(function(){ appendCard.call(null, '[data-id='+(self.data('id')+1)+']')}, 2000); } }); }); </script> </head> <body> <div class="wrapper"> <div id="User" class=""> <p>User</p> <div class="ssUser ui-state-default">Блок 1</div> <div class="ssUser ui-state-default">Блок 2</div> <div class="ssUser ui-state-default">Блок 3</div> <div class="ssUser ui-state-default">Блок 4</div> <div class="ssUser ui-state-default">Блок 5</div> <div class="ssUser ui-state-default">Блок 6</div> </div> <div id="trash" class="game-field" data-id="1"><div class="zip" data-id="2">сюда упадёт карта Comp</div></div> <div id="Comp" class=""> <p>Comp</p> <div class="ssComp ui-state-default">Блок 1</div> <div class="ssComp ui-state-default">Блок 2</div> <div class="ssComp ui-state-default">Блок 3</div> <div class="ssComp ui-state-default">Блок 4</div> <div class="ssComp ui-state-default">Блок 5</div> <div class="ssComp ui-state-default">Блок 6</div> </div> </div> </body> </html> |
Рони, вот это ты крут... взял и замострячил макет за меня ... спасибо!
Вообщем, пытаясь внести правки в твой код описываю проблему, я наконец понял в чём вся загвоздка. Вся проблема в переменной insertComp, которую я инициализирую и потом постоянно к ней обращаюсь. Т.к. используется метод сетТаймаут, карта ssComp помещается в нужную ячейку спустя какое-то время (в данном случае 2 сек), а переменная инициализируется сразу, когда карты ещё нет в ячейке, и поэтому в переменную записывается undefined. Соответсвенно, вопрос: Как мне инициализировать переменную спустя 3сек? setTimeout в этом случае не работает... var insertComp = $('[data-id='+(self.data('id')+1)+'] .ssComp') |
Всё, разобрался до конца!!!
setTimeout оказывается работает, только нужно сначала определить переменную за пределами метода, а потом уже в методе setTimeout присвоить ей какое-либо значение. Рони, спасибо за помощь!!! |
Часовой пояс GMT +3, время: 12:11. |