Таблица лидеров


Популярные публикации

Отображаются наиболее популярные публикации начиная с 24.08.2017 во всех областях

  1. 3 балла
    Как вариант при выводе часть строки кодировать в base64 и сохранять, к примеру, в data атрибуте, а при клике раскодировать обратно и вставить в нужное место. console.log(window.btoa('+380 (95) 632-42-51'));// вернет "KzM4MCAoOTUpIDYzMi00Mi01MQ==" console.log(window.atob("KzM4MCAoOTUpIDYzMi00Mi01MQ=="));// вернет "+380 (95) 632-42-51"
  2. 1 балл
    Убрал overflow-x: hidden; у body или html и прилипание заработало. Нашел статью, там сказано что не следует использовать свойство overflow: hidden; overflow: scroll; или overflow: auto; для контейнера прилипающего блока. И также не следует задавать высоту для контейнера.
  3. 1 балл
    http://apps.eky.hk/css-triangle-generator/
  4. 1 балл
    https://jsfiddle.net/5pwxwpsf/2/ Тут есть нюансы: screen.width - размер экрана, а не окна браузера, скорее всего оно вам поможет если вы будете тестить на меньшем экране в реальности. document.body.getElementsByTagName возвращает коллекцию элементов, а не сам элемент как это делает getElementById, т.к. с id подразумевается только 1 элемент, а элементов с одинаковым тегом может быть много
  5. 1 балл
    Назовите его "Моя лапочка!" ну или "Моя Прелесть!" :)
  6. 1 балл
    Всё просто : .element{ background-image:url(images/image.png) no-repeat; background-size:cover; //Задаешь сразу размеры изображения относительно границ блока , можно использовать свойство "contain" background-postion:center center; // Плюс отцентрируй изображение width:300px; // Задаешь ширину и высоту блока чтобы увидеть изображение height:300px; } И уже в процессе играешься со свойствами background-size , width, height и background-position , пока не добьешься желаемого отображения .
  7. 1 балл
    Вот наглядный пример : При переносе свойства "transition" с состояния "hover" в дефолтный стиль ссылки , плавность не будет пропадать при отведении мышки с элемента меню.
  8. 1 балл
    Transition надо на исходное состояние вешать а не на ховер, чтоб вот такой фигни как у тебя сейчас не было.
  9. 1 балл
    Блочному элементу, у которого есть фоновое изображение, но нет контента, требуется задать свойства влияющие на его высоту. Как правило указывается ширина/высота
  10. 1 балл
    с ul.cat-drop-list z-index убрать вообще а на родителя с box-shadow .cat-toggler-holder поставить z-index: 5
  11. 1 балл
    Из-за класса .top-text, которому вы задаете marin: 40px; ломаются колонки. Не нужно трогать колонки (блоки с классами .col-**-**), лучше создать новый блок внутри колонки и менять его а не саму колонку. https://codepen.io/corvus-007/pen/EwPYpX?editors=1100
  12. 1 балл
    может через box-shadow сделать эти градиенты, вот пример:
  13. 1 балл
  14. 1 балл
    php на IIS тоже устанавливается как бы. А если не хотите то есть родной ASP.NET
  15. 1 балл
    У меня был матовый TN и сейчас глянцевая Retina. Формат работы у меня около туристического - работаю там где получается (хостел, коворкинг, мащина, кафе, аэропорт, и так далее). Так вот современные глянцы, не смотря на блики, намного приятнее на глаз чет матовые. Конечно, если солнце будет лупить в спину и слепить экран то ничего не увидеть. В остальных случаях картинка читабельна более чем.
  16. 1 балл
    Честно говоря по экранам за рамками техники Apple моё мнение будет иметь малый вес, так как опыта работы с ними с ~ 2013 г. нет. Но, можно сделать выборку по характеристикам, а потом уже читать обзоры на каждую модель и выбрать ожидаемый аппарат
  17. 1 балл
    Любая PLS/IPS будет лучше TN Из-за высокой контрастности экрана надобность в матовых, как мне кажется, отпала. Преимущественно все панели — глянцевые. Из личного опыта: Приблизительно лет 7+ тому назад я перешел на вёрстку с ноутбука приобрёл Dell Vostro 3700 17" с MacOS. Спустя несколько лет пересел на MBP 15" и с тех пор (поменяв ноут трижды) так и работаю в таком форм-факторе. У меня есть опыт работы как только за ноутбуком так и ноут + два 24" монитора — каждый их этих вариантов превосходен под определенную задау. На пример: Работать за одним ноутбуком удобно в случае когда требуется высокая мобильность, такая как в постоянном туризме. Кол-во техники ограничено весом и объемом. Так же ноутбук удобно использовать когда требуется частичная мобильность — в формате дом-офис. Дома можно выполнять простые задаче, в офисе подключить 2+ монитора и тем самым увеличить экранное полотно (жутко удобно). Недавно стало возможно использовать мобильные устройства в качестве доп. мониторов при помощи Duet Display. На iPad Air 2 работает с тормозами (Pro еще не пробовал). Но всё же мобильный вариант второго монитора — это удобнее чем без него. Что может быть не удобно (все пункты индивидуальны, могут проявляться а могут и нет): Клавиатура — кнопки, всё же, расположены ближе чем на обычной клавиатуре (не смотря на то что в 15" ноутбуке полноразмерная клавиатура). Это может сказываться дискомфортом на запястья (усталость, оттёки, болевые ощущения). Решается элементарно — приобртением полноразмерной клавиатуры (неоднократно такое вижу, обычное дело) Низкая посадка монитора — из-за того что ноут размещается на столе, то на экран требуется смотреть сверху в низ. Решение — подставка под ноутбук + клавиатура на стол. Такое встречается реже но тоже ничего особенного Если выбирать ноутбук на 17", работать за ним стационарно и пренебрегать возможностью подключения доп. мониторов то следует учесть вес устройства. Такие ноуты давно не модны, спрос низкий, подобрать что-то качественное довольно сложно. Большой монитор и более просторная клавиатура — основные достоинства такого устройства. Продать такой гаджет так же будет сложнее.
  18. 1 балл
    FullHD 15" - 17" и в общем-то вполне норм. Верстала и на более мелких экранах, но это больше экстрим ) По большому счету разницы особой нет.
  19. 1 балл
    Тоже работаю на ноуте пока. Мощности хватает и цветопередачи тоже, а на больших экранах верстать помогают "инструменты разработчика" в браузере. Бесит конечно, что маленькая клавиатура и экран, по сравнению с ПК Особенно первое время, но потом привыкаешь. USB-клавиатура наполовину решила вопрос. Беспроводная мышка действительно удобней, если ноут куда-то носишь. Я ее долго недооценивала, пока не попробовала. Других напрягов не вижу. PS я не спец в компьютерах, просто поделилась опытом
  20. 1 балл
    Input снаружи блока делайте, а label внутри. Т.к в цсс нельзя обращаться к родительскому элементу.
  21. 1 балл
    У меня ноут, не так удобна клавиатура .. 15.6" экран, для меня это также мало .. И я по своей глупости купил ноутбук с не очень хорошим экраном, плохая цветопередача ..
  22. 1 балл
    Наверное ты всё таки прав, если это действительно осмысленное слово, которое к тому же должно быть проиндексировано, разбивать его тегом <BR> не стоит.
  23. 1 балл
    Очень даже как https://jsfiddle.net/o5ants3u/
  24. 1 балл
    можно попробовать через label попробовать https://jsfiddle.net/q4gz36mp/
  25. 1 балл
  26. 1 балл
    $('.sh_nmr').click(function(){ yaCounter42664049.reachGoal('target_1'); //остальной код обработчика });
  27. 1 балл
    я похоже хрень написал) давайте ещё раз - у каждого товара есть возможность выбрать товары, которые будут под ним показываться. так? если так то вам нужно как раз таки получить то свойство. там будет массив. и в фильтр сунуть $arrFilter['ID'] = $arrID; если указан массив, то будет выборка тех id которые указаны. при привязке элементов, насколько помню именно id и возвращается
  28. 1 балл
    то что под спойлером ненадо) смотрите в шаблоне, какая переменная фильтра используется. и в неё дописываете что то вроде global $arrFilter; $arrFilter["PROPERTY_ЗДЕСЬКОДСВОЙСТВА_VALUE"] = array($arResult['ID']); т.е. ищет товары у которых в данном св-ве указан текущий товар я просто не вкурсе про какой модуль вы пишите. это как примерный вариант решения
  29. 1 балл
    да без разницы, этож пример, можно изменить как угодно.
  30. 1 балл
    Вот не понимаю зачем, там же в документации всё разжёвано .
  31. 1 балл
    Цикл статей от Влада Мержевича: https://webref.ru/layout/bootstrap
  32. 1 балл
    За одну и туже работу разные программисты возьмут разные суммы. Вопрос в стоимости времени, у одного нет работы и куча свободного времени и он готов сделать за 10к, а другой занят под завязку и его время стоит дорого и он просит в 2-3 раза больше первого. Это нормально. Плюс качество работы может отличаться. Вот вам наглядное пособие, о котором не стоит забывать =)
  33. 1 балл
    плохой способ скрывания номера, я его все равно вижу, ну а боты и подавно (а скрывать как раз от них нужно) лучше всего использовать шифрование или подгрузку через ajax https://jsfiddle.net/tm0xwjp0/
  34. 1 балл
    Если правильно понял проблему, то у header добавьте - top: 0; И дальше main можно двигать без всяких "<br>"
  35. 1 балл
    работаю со Vue с 2015 года. Когда он еще в первой версии был и хайпа не было. Впечатление исключительно положительное: низкий порог входа (в отличии от ангуляра или реакта), простые вещи делаются просто, декларативность в шаблонах по мне так огромный плюс (т.е. императивного кода на той же jQuery будет в разы больше и он будет менее понятным). Я когда с докладом выступал по этой либе, отметил, что на мой взгляд, это следующий швейцарский нож для веба (как когда-то была jQuery). Иными словами, получит очень широкое распространение и будет стандартом дефакто. не слушай тех, кто говорит писать на чистом JS. В большинстве случаев это выпендреж. Практика показывает, что целесообразнее сразу подключать библиотеки на подобии Vue, т.к. бизнес-логика имет свойство меняться/усложняться. Кроме того, Vue еще хороша тем, что ее можно применять точечно. Т.е. ты можешь насоздавать таких вот "компонент" и навешать их по селекторам на страницу. к предыдущему комментарию могу добавить, что: 1. в целом подход правильный, ты не изобретаешь велосипед 2. `new Vue({` будет достаточно (чем меньше глобальных переменных, тем меньше вероятность выстрелить себе в ногу) 3. названия методов, обычно, начинаются с глаголов или просто глаголы. В твоем случае подойдет `setValue()`. Но это уже если совсем по правильному
  36. 1 балл
    $tablet-width: 'min-width: 750px'; .main-header { width: 100px; height: 100px; @media (#{$tablet-width}) {} }
  37. 1 балл
    Круги тоже можно svg элементами сделать. На счёт адаптации. Свободное пространство слева и справа будет обрезаться я так понимаю. Когда до кругов дойдёт их перестроить покучнее ( 1 медиа запрос ). А дальше ( как не будут помещаться ) убирать всю эту хреноверть и делать списком.
  38. 1 балл
    Зачем canvas. Просто картинкой вставьте (я бы SVG использовал здесь). Кружки абсолютно спозиционируйте поверх.
  39. 1 балл
    у вас display:none; используется, он отключает любые анимации, т.к. не анимируется. Я бы делал так https://jsfiddle.net/rms32upL/, но здесь вам нужен только принцип анимации.
  40. 1 балл
    надеюсь этого хватит? https://jsfiddle.net/n2j5wbeh/
  41. 1 балл
    Идею я не до конца уловил. ПДД насколько мне известны одинаковые во всех городах. Загружается быстро. Нет, пользоваться не удобно. У меня при переходах по разделам на весь экран картинка, которую нужно скролить что бы узнать что же там открылось. Могу тебе точно сказать что пользователи с такого сайта буду просто уходить решив что он не работает. С клавиатуры пользоваться не удобно. Как тебе не стыдно отключать айтлайны ! Ты же разработчик должен понимать что люди и без мышки по сайтам ходят ! Добавил. База городов должна быть. При вводе появляется список подходящий под введённые значения ( произвольные значения не принимаются ). Ну и в целом есть над чем поработать конечно.
  42. 1 балл
    Все зависит от типа столбца, вот примеры: Структура таблицы CREATE TABLE IF NOT EXISTS `test` ( `d1` date NOT NULL, `d2` datetime NOT NULL, `d3` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `d4` time NOT NULL, `d5` year(4) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; Заносим данные INSERT INTO `dbtest`.`test` (`d1`, `d2`, `d3`, `d4`, `d5`) VALUES ('2017-08-25', '2017-08-25 00:00:00', CURRENT_TIMESTAMP, '00:00:00', '2017');
  43. 1 балл
    Вопрос про виртуального коня в вакууме, стоило непосредственно уточнить у них, т.к. для каждого проекта может требоваться разное окружение. Возможно имелось ввиду node.js - установка/настройка сервера/модулей / gulp/grunt/webpack / angular/react/vue и т.п.
  44. 1 балл
    под "поднять фронтэнд" - наверно подразумевается быстро нарисовать морду какому нибудь сайту
  45. 1 балл
    Надо было у них спросить) А вообще, бежать надо от туда, где такие вопросы задают, без какого либо пояснения.
  46. 1 балл
    https://codepen.io/anon/pen/ZJrqBp
  47. 1 балл
    "Создать land-page.php, туда загрузить html/css, далее этот шаблон будет виден в редакторе?" - появится в админке (меню: внешний вид ->редактор). "тогда создать страницу, унаследовав стиль из этого шаблона?" - да, для нужной страницы выбрать ваш шаблон. "Какие подводные камни следуют из этого способа?" - подводные камни: не забыть подключить необходимые вам функции из ВП. дальше не важно, можно хоть разных 100 хедеров подключать для разных страниц, или 100 футеров (главное не запутаться потом)
  48. 1 балл
    Доброго тебе дня, друг. Данную тему написать меня сподвигло больше кол-во однотипных тем на тему "Ищу наставника", "Нужен учитель" и тому подобное. Одной из причин тому статья "Путь верстальщика" от автора Максима Усачева, в которой он делится своим безценным жизненным опытом. Я постараюсь пролить свет на некоторые важные (в рамках данной темы) моменты из статьи. Макс пришел на форум имея минимум знаний в сфере разработки и имея большое желание учится. Он упоминает про наставника, но большинство читателей, к сожалению, трактуют данную информацию извращенно. Это не путь к руководству, это всего лишь опыт который получил автор статьи. Нужто ты думаешь, что если и у тебя будет наставник, то ты выучишь все в 2, 3, 10 раз быстрее? Или больше? Или качественнее? Более того, наставник может научить тебя плохому. Наставник в разрезе данной статьи -- это специалист, который допустил огромное кол-во ошибок, который "кровью и потом" работал до глубокой ночи ведя жесткую борьбу с браузерами для достижения своей цели. И хотя результат боя не всегда был на стороне разработчика, войну он всегда выигрывает. Зачем тебе наставник? Что бы он говорил тебе где ты ошибся? Или как надо было сверстать тот или иной блок? Или что лучше использовать float или inline-block? И что это тебе даёт? Набор правил и готовых решений как надо делать и как не надо! Но ведь ты знаешь что каждый сайт уникален, и везде есть нюансы. И ты хочешь каждый раз обращаться к учителю что бы тот подсказал тебе как решить задачу?... Разве ты не хочешь научится думать и понимать чем живет вёрстка? Как она устроена, что из себя представляют слои, понимать и визуализировать и процесс разработки? Понимать "физику" работы блоков на страницы, как и почему они влияют на остальные? Предугадывать на будущее как потянется страница/блок в случае наполнении его контентом. Уметь за считанные секунды в голове разложить огромный сайт на слои? Достичь понимания вёрстки как некой сущности, которая является единым целым с тобой. Другими словами -- ты желаешь женится на прекрасной девушке, со своим темпераментом, безграничными возможностями, уникальным подходом. Она способна подстраиваться под тебя так как ты этого хочешь, когда только пожелаешь. Она согласна выполнять твои прихоти, она не переборчива и соглашаеться на любые твои даже самые безумны идеи. И не смотря на это у неё свой уникальный неповторимый характер. Она бывает упёртая и неприступная, но всегда можно найти с ней общий язык. Если ты знаешь как с ней общаться, понимаешь как она думает, чувствуешь её, чувствуешь то что чувствует она, видишь то что видит она. Если ты слышишь и понимаешь её, то невозможно будет представить цели, которую вы вместе не смогли бы достичь. Ты хочешь женится на самой прекрасной девушке, но спать с ней будет твой наставник! Я не говорю что ты не сможешь стать специалистом без него. Большинство разработчиков, которые поддерживают этот форум, не имели никаких учителей. Все чего они добивались -- все делали сами. Для того что бы достичь тех же высот тебе надо повторить успех людей которые это уже сделали! Это не просто, но это реально! Никто за тебя этого не сделает, никому кроме тебя это не надо. Далее по списку: 1. Перед тем как открыть свою тему в поисках Наставника, пройдись по темам которые создали раньше. 2. Ты ищешь классного специалиста, который будет тебе помогать тебе, обучать, давать советы, делится опытом и знаниями. Ты этого хочешь? А ты можешь ответить на вопрос - зачем вообще кому-то это делать? Зачем, для чего, почему кто-то должен согласится тратить на тебя время? Ведь тебе нужен не новичек как ты, а тот, у кого за плечами багаж опыта, ведь так? Так вот чем ты отличаешься от других таких же жаждущих что бы вкладывать в тебя время и делится нажитым опытом? Пойми правильно, дело то не в деньгах, тут важна идея. 3. Теперь по теме. 3.1. Зачем тебе вообще это направление? Что оно тебе даст? Что ты нашел в вёрстке? 3.2. Если ты считаешь что вместо тебя будет гуглить наставник -- ты ошибаешься. Гугл -- лучший твой помошник. Умеешь правильно составить запрос - умеешь решать задачи. Очень часто бывает так, что поиск решения намного приоритетнее, нежели знания. 3.3. Если ты считаешь что наставник будет тебе давать задания -- ты сильно ошибаешься. Тебе надо -- ты ищи макет, а наставник тебе может написать ТЗ, а потом сказать какая ты бестолочь, указав на твои ошибки в результате -- а это бесценно! 3.4. Любой опыт забирает много времени. Ты не станешь специалистом через год-два! Ты уверен что у тебя вообще хватит сил на то что бы потратить ~5 лет своей жизни в интенсиве и "выкарабкатся" на уровень, когда ты сможешь сверстать страницу (предположим сайта новостей с огромным кол-во блоков) в уме за считаные секунды? Ты уверен что ты хочешь потратить часть своей жизни на изучение вёрстки? ps: нет ничего невозможного, было бы желание
  49. 1 балл
    Есть некроссбраузерное решение, только для тех, кто понимает CSS3-селекторы. <!DOCTYPE HTML> <title>Spoiler</title> <style type="text/css" media="screen"> #spoiler { display: none; } #spoiler:target { display: block; } </style> <a href="#spoiler">Просмотреть текст</a> <div id="spoiler"> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </div>
  50. 1 балл
    Обрабатываем клик по клетке Итак, у нас уже почти все готово для полноценной игры. Осталось только обработать клик по клетке на доске. Давайте создадим метод который назовем showInfo, внутри данного метода мы будем проверять проиграл ли уже пользователь, что отобразить на клетке и т.п. Метод будет принимать в качестве параметра элемент на котором кликнули: var MyMineSweeper = { init: function(o) { this.W = o ? o.W : 7; this.H = o ? o.H : 7; this.bombs = o ? o.bombs : Math.floor(this.W * this.H / 4); this.placedBombs = this.bombs; this.generateGUI(); }, generateGUI: function() { if (!this.game) { this.game = this.writeMainContainer(); this.gameCont = document.createElement('table'); this.gameCont.className = 'game-cont'; this.gameCont.insertRow(0); this.gameCont.insertRow(1); this.gameMenu = this.gameCont.rows[0].insertCell(0); this.gameMenu.rowSpan = 2; this.gameMenu.className = 'game-menu'; this.gameMenu.appendChild(document.createTextNode('Ширина поля:')); this.gameMenu.appendChild(document.createElement('br')); this.WInput = document.createElement('input'); this.WInput.type = 'text'; this.gameMenu.appendChild(this.WInput); this.gameMenu.appendChild(document.createTextNode('Высота поля:')); this.gameMenu.appendChild(document.createElement('br')); this.HInput = document.createElement('input'); this.HInput.type = 'text'; this.gameMenu.appendChild(this.HInput); this.gameMenu.appendChild(document.createTextNode('Кол-во мин:')); this.gameMenu.appendChild(document.createElement('br')); this.BInput = document.createElement('input'); this.BInput.type = 'text'; this.Init = document.createElement('input'); this.Init.type = 'button'; this.Init.value = 'Старт'; this.Init.className = 'game-start-button'; this.gameMenu.appendChild(this.BInput); this.gameMenu.appendChild(this.Init); this.gameStats = this.gameCont.rows[0].insertCell(1); this.gameField = this.gameCont.rows[1].insertCell(0); this.gameField.className = 'game-field'; this.game.appendChild(this.gameCont); } if (this.board) { this.board.parentNode.removeChild(this.board); this.board = null; } this.board = this.generateField(); this.board.cellSpacing = 0; this.board.className = 'game-board'; this.gameField.appendChild(this.board); this.gameStats.innerHTML = 'Новая игра: поле ' + this.W + 'x' + this.H + ', ' + this.bombs + 'мин'; this.setupEvents(); }, setupEvents: function() { var self = this; var buttonClick = function() { self.init({W: self.WInput.value, H: self.HInput.value, bombs: self.BInput.value}); } Event.add(this.Init, 'click', buttonClick); }, generateField: function() { var self = this; var table = document.createElement('table'); for (var i = 0; i < this.H; i++) { var r = table.insertRow(i); for (var j = 0; j < this.W; j++) { var c = r.insertCell(j); c.num = 0; c.index = [i, j]; c.clickHandler = function() { self.showInfo(this); } Event.add(c, 'click', c.clickHandler); } } do { var hNum = this.rand(0, this.H - 1); var wNum = this.rand(0, this.W - 1); if (!table.rows[hNum].cells[wNum].bomb) { table.rows[hNum].cells[wNum].num = null; table.rows[hNum].cells[wNum].bomb = true; this.placedBombs--; } } while (this.placedBombs > 0); for (var i = 0, len = table.rows.length; i < len; i++) { for (var j = 0, len2 = table.rows[i].cells.length; j < len2; j++) { if (table.rows[i].cells[j].bomb) { this.placeNumbers(table, j, i); } } } return table; }, placeNumbers: function(t, x, y) { if (x > 0) { t.rows[y].cells[x - 1].num++; } if (x < this.W - 1) { t.rows[y].cells[x + 1].num++; } if (x > 0 && y > 0) { t.rows[y - 1].cells[x - 1].num++; } if (y > 0) { t.rows[y - 1].cells[x].num++; } if (y > 0 && x < this.W - 1) { t.rows[y - 1].cells[x + 1].num++; } if (x > 0 && y < this.H - 1) { t.rows[y + 1].cells[x - 1].num++; } if (y < this.H - 1) { t.rows[y + 1].cells[x].num++; } if (x < this.W - 1 && y < this.H - 1) { t.rows[y + 1].cells[x + 1].num++; } }, showInfo: function(elem) { if (!elem.bomb) { this.openCell(elem); } else { this.openCell(elem); this.gameOver(); } }, openCell: function(elem) { if (!elem.bomb) { if (elem.num > 0) { elem.innerHTML = '<b>' + elem.num + '</b>'; } else { elem.innerHTML = ' '; } switch (elem.num) { case 1: elem.style.color = 'blue'; break; case 2: elem.style.color = 'green'; break; case 3: elem.style.color = 'red'; break; case 4: elem.style.color = 'dakrblue'; break; case 5: elem.style.color = 'pink'; break; case 6: elem.style.color = 'navy'; break; case 7: elem.style.color = 'brown'; break; case 8: elem.style.color = 'grey'; break; default: elem.style.color = 'black'; } } else { elem.innerHTML = '<b>M</b>'; } elem.style.background = '#d8e0ec'; }, writeMainContainer: function() { var html = '<div id="game" class="game"></div>'; document.writeln(html); return document.getElementById('game'); }, rand: function(min, max) { min = parseInt(min); max = parseInt(max); return Math.floor(Math.random() * (max - min + 1)) + min; } } Обрабатываем Game Over Теперь подумаем, что должно происходить когда юзер нарвался на мину: 1) нам нужно показать все мины на доске, 2) нам нужно снять все слушатели событий с клеток, чтобы пользователь не мог никуда больше кликнуть пока не начнет новую игру, 3) нам нужно уведомить пользователя о том что он проиграл. Реализуем все в коде: gameOver: function() { // пробегаемся циклом по доске for (var i = 0, len1 = this.board.rows.length; i < len1; i++) { // цикл по вертикали for (var j = 0, len2 = this.board.rows[i].cells.length; j < len2; j++) { // цикл по горизонтали if (this.board.rows[i].cells[j].bomb) { // если на клетке стоит мина // показываем мину this.openCell(this.board.rows[i].cells[j]); } // снимаем обработчик события "click" с каждой клетки Event.remove(this.board.rows[i].cells[j], 'click', this.board.rows[i].cells[j].clickHandler); } } // красим ячейку статистики в красный цвет this.gameStats.style.background = 'red'; // и говорим юзеру, что он подорвался this.gameStats.innerHTML = '<b>БАБАХ!!! GAME OVER!</b>'; } У нас все готово для полноценной игры. Ура! Однако радоваться рано. Если посмотреть на настоящего сапёра, то можно увидет одну особенность - если мы кликнем на клетку рядом с которой нет мин (т.е. номер на ней 0), то откроются все соседние пустые клетки, т.е. все клетки, граничащие с открытой, рядом к которыми тоже нет мин. То есть происходит так называемый каскад. Пожалуй это будет самая сложная функция в нашем сапёре. Итак... Реализуем каскад Вы себе даже не представляете как долго я бился на этим методом. Что я только не делал, где только не гуглил И вот, наконец я допер, что в нелегком деле каскадирования нам поможет рекурсия! Что такое рекурсия? Это когда функция вызывает сама себя. Что обязательно должно быть в рекурсивной функции? Первым делом в рекурсивной функции должен быть продуман выход из нее, чтобы не было бесконечного зацикливания. В принципе это все, что нужно знать о рекурсии, но без примера все равно сложно это понять, поэтому я вам приведу пример простейшей рекурсии: // функция принимает в качестве параметра начальное число и конечное число function myRecursion(num, maxNum) { /* * первым делом продумываем * выход из бесконечной рекурсии */ if (num > maxNum) { // если начальное число больше конечного // выходим из функции return; } // пишем в документ текущую цифру document.write(num + ', '); // увеличиваем число на 1 num++; // вызываем саму себя (рекурсивный вызов) myRecursion(num, maxNum); } myRecursion(0, 5); // выведет 0, 1, 2, 3, 4, 5 Ну вот, теперь вы должны представлять что такое рекурсия и мы можем начать писать наш метод-каскад, назовем его roll, метод будет принимать в качестве параметров координаты клетки на доске: roll: function(x, y) { /* * первым делом продумываем * выход из бесконечной рекурсии */ // если x или y выходят за границы доски if (x < 0 || y < 0 || x >= this.W || y >= this.H) { // выходим return; } // открываем клетку this.openCell(this.board.rows[y].cells[x]); // если рядом с клеткой есть бомба (т.е. номер в ней больше 0) if (this.board.rows[y].cells[x].num > 0) { // вставляем в клетку цифру this.board.rows[y].cells[x].innerHTML = '<b>' + this.board.rows[y].cells[x].num + '</b>'; // выходим return; } // если мы уже эту клетку проверяли if (this.board.rows[y].cells[x].check) { // выходим return; } // отмечаем, что эту ячейку мы уже проверяли this.board.rows[y].cells[x].check = true; // далее пошла рекурсия this.roll(x + 1, y); // делаем шаг вправо this.roll(x - 1, y); // делаем шаг влево this.roll(x, y + 1); // делаем шаг вниз this.roll(x, y - 1); // делаем шаг вверх this.roll(x - 1, y - 1); // делаем шаг вверх влево this.roll(x + 1, y - 1); // делаем шаг вверх вправо this.roll(x - 1, y + 1); // делаем шаг вниз влево this.roll(x + 1, y + 1); // делаем шаг вниз вправо } Вызывать наш каскад будем в методе showInfo, для этого нам его надо будет немного изменить, добавив дополнительные проверки: showInfo: function(elem) { if (!elem.bomb) { // если в клетке нет мины if (elem.num > 0) { // если рядом есть мины (номер больше 0) // открываем клетку this.openCell(elem); } else { // если рядом нет ни одной мины /* * запускаем каскад * тут нам и пригодилось дополнительное * свойство хранимое в клетке (index), * в котором храняться в виде массива * координаты клетки на доске */ this.roll(elem.index[1], elem.index[0]); } } else { // если в клетке мина // открываем клетку this.openCell(elem); // завершаем игру this.gameOver(); } } Вот и все. Теперь у нас готов полноценный сапер. Он конечно не совсем похож на того сапера который присутствует в винде. В нем нельзя отмечать места с предполагаемой миной и нет счетчика времени, зато в нем можно варьировать уровень сложности и размер доски по своему усмотрению, а все недостатки легко доделать. Главное вы теперь знаете устройство такой замечательной логической игры как сапёр! Ах да, вот полный код нашего сапера: var MyMineSweeper = { init: function(o) { this.W = o ? o.W : 7; this.H = o ? o.H : 7; this.bombs = o ? o.bombs : Math.floor(this.W * this.H / 4); this.placedBombs = this.bombs; this.generateGUI(); }, generateGUI: function() { if (!this.game) { this.game = this.writeMainContainer(); this.gameCont = document.createElement('table'); this.gameCont.className = 'game-cont'; this.gameCont.insertRow(0); this.gameCont.insertRow(1); this.gameMenu = this.gameCont.rows[0].insertCell(0); this.gameMenu.rowSpan = 2; this.gameMenu.className = 'game-menu'; this.gameMenu.appendChild(document.createTextNode('Ширина поля:')); this.gameMenu.appendChild(document.createElement('br')); this.WInput = document.createElement('input'); this.WInput.type = 'text'; this.gameMenu.appendChild(this.WInput); this.gameMenu.appendChild(document.createTextNode('Высота поля:')); this.gameMenu.appendChild(document.createElement('br')); this.HInput = document.createElement('input'); this.HInput.type = 'text'; this.gameMenu.appendChild(this.HInput); this.gameMenu.appendChild(document.createTextNode('Кол-во мин:')); this.gameMenu.appendChild(document.createElement('br')); this.BInput = document.createElement('input'); this.BInput.type = 'text'; this.Init = document.createElement('input'); this.Init.type = 'button'; this.Init.value = 'Старт'; this.Init.className = 'game-start-button'; this.gameMenu.appendChild(this.BInput); this.gameMenu.appendChild(this.Init); this.gameStats = this.gameCont.rows[0].insertCell(1); this.gameField = this.gameCont.rows[1].insertCell(0); this.gameField.className = 'game-field'; this.game.appendChild(this.gameCont); } if (this.board) { this.board.parentNode.removeChild(this.board); this.board = null; } this.board = this.generateField(); this.board.cellSpacing = 0; this.board.className = 'game-board'; this.gameField.appendChild(this.board); this.gameStats.innerHTML = 'Новая игра: поле ' + this.W + 'x' + this.H + ', ' + this.bombs + 'мин'; this.WInput.value = this.W; this.HInput.value = this.H; this.BInput.value = this.bombs; this.gameStats.style.background = '#bfd3ed'; this.setupEvents(); }, setupEvents: function() { var self = this; var buttonClick = function() { self.init({W: self.WInput.value, H: self.HInput.value, bombs: self.BInput.value}); } Event.add(this.Init, 'click', buttonClick); }, generateField: function() { var self = this; var table = document.createElement('table'); for (var i = 0; i < this.H; i++) { var r = table.insertRow(i); for (var j = 0; j < this.W; j++) { var c = r.insertCell(j); c.num = 0; c.index = [i, j]; c.clickHandler = function() { self.showInfo(this); } Event.add(c, 'click', c.clickHandler); } } do { var hNum = this.rand(0, this.H - 1); var wNum = this.rand(0, this.W - 1); if (!table.rows[hNum].cells[wNum].bomb) { table.rows[hNum].cells[wNum].num = null; table.rows[hNum].cells[wNum].bomb = true; this.placedBombs--; } } while (this.placedBombs > 0); for (var i = 0, len = table.rows.length; i < len; i++) { for (var j = 0, len2 = table.rows[i].cells.length; j < len2; j++) { if (table.rows[i].cells[j].bomb) { this.placeNumbers(table, j, i); } } } return table; }, placeNumbers: function(t, x, y) { if (x > 0) { t.rows[y].cells[x - 1].num++; } if (x < this.W - 1) { t.rows[y].cells[x + 1].num++; } if (x > 0 && y > 0) { t.rows[y - 1].cells[x - 1].num++; } if (y > 0) { t.rows[y - 1].cells[x].num++; } if (y > 0 && x < this.W - 1) { t.rows[y - 1].cells[x + 1].num++; } if (x > 0 && y < this.H - 1) { t.rows[y + 1].cells[x - 1].num++; } if (y < this.H - 1) { t.rows[y + 1].cells[x].num++; } if (x < this.W - 1 && y < this.H - 1) { t.rows[y + 1].cells[x + 1].num++; } }, showInfo: function(elem) { if (!elem.bomb) { if (elem.num > 0) { this.openCell(elem); } else { this.roll(elem.index[1], elem.index[0]); } } else { this.openCell(elem); this.gameOver(); } }, openCell: function(elem) { if (!elem.bomb) { if (elem.num > 0) { elem.innerHTML = '<b>' + elem.num + '</b>'; } else { elem.innerHTML = ' '; } switch (elem.num) { case 1: elem.style.color = 'blue'; break; case 2: elem.style.color = 'green'; break; case 3: elem.style.color = 'red'; break; case 4: elem.style.color = 'dakrblue'; break; case 5: elem.style.color = 'pink'; break; case 6: elem.style.color = 'navy'; break; case 7: elem.style.color = 'brown'; break; case 8: elem.style.color = 'grey'; break; default: elem.style.color = 'black'; } } else { elem.innerHTML = '<b>M</b>'; } elem.style.background = '#d8e0ec'; }, gameOver: function() { for (var i = 0, len1 = this.board.rows.length; i < len1; i++) { for (var j = 0, len2 = this.board.rows[i].cells.length; j < len2; j++) { if (this.board.rows[i].cells[j].bomb) { this.openCell(this.board.rows[i].cells[j]); } Event.remove(this.board.rows[i].cells[j], 'click', this.board.rows[i].cells[j].clickHandler); } } this.gameStats.style.background = 'red'; this.gameStats.innerHTML = '<b>БАБАХ!!! GAME OVER!</b>'; }, roll: function(x, y) { if (x < 0 || y < 0 || x >= this.W || y >= this.H) { return; } this.openCell(this.board.rows[y].cells[x]); if (this.board.rows[y].cells[x].num > 0) { this.board.rows[y].cells[x].innerHTML = '<b>' + this.board.rows[y].cells[x].num + '</b>'; return; } if (this.board.rows[y].cells[x].check) { return; } this.board.rows[y].cells[x].check = true; this.roll(x + 1, y); this.roll(x - 1, y); this.roll(x, y + 1); this.roll(x, y - 1); this.roll(x - 1, y - 1); this.roll(x + 1, y - 1); this.roll(x - 1, y + 1); this.roll(x + 1, y + 1); }, writeMainContainer: function() { var html = '<div id="game" class="game"></div>'; document.writeln(html); return document.getElementById('game'); }, rand: function(min, max) { min = parseInt(min); max = parseInt(max); return Math.floor(Math.random() * (max - min + 1)) + min; } } Всем спасибо за внимание, надеюсь урок был вам полезен. Предложения, замечания и т.п. пишите в личку.
Таблица лидеров находится в часовом поясе Киев/GMT+03:00