diff --git a/dnd-content.hbs b/dnd-content.hbs
new file mode 100644
index 0000000..b11e31b
--- /dev/null
+++ b/dnd-content.hbs
@@ -0,0 +1 @@
+
diff --git a/dnd.hbs b/dnd.hbs
new file mode 100644
index 0000000..fd47fc0
--- /dev/null
+++ b/dnd.hbs
@@ -0,0 +1,12 @@
+
+
+
+
+ {{htmlWebpackPlugin.options.title}}
+
+
+
+ {{> "./dnd-content.hbs"}}
+
+
+
diff --git a/src/dnd.js b/src/dnd.js
new file mode 100644
index 0000000..9ee087c
--- /dev/null
+++ b/src/dnd.js
@@ -0,0 +1,91 @@
+/* Задание со звездочкой */
+
+/*
+ Создайте страницу с кнопкой.
+ При нажатии на кнопку должен создаваться div со случайными размерами, цветом и позицией на экране
+ Необходимо предоставить возможность перетаскивать созданные div при помощи drag and drop
+ Запрещено использовать сторонние библиотеки. Разрешено пользоваться только тем, что встроено в браузер
+ */
+
+/*
+ homeworkContainer - это контейнер для всех ваших домашних заданий
+ Если вы создаете новые html-элементы и добавляете их на страницу, то дабавляйте их только в этот контейнер
+
+ Пример:
+ const newDiv = document.createElement('div');
+ homeworkContainer.appendChild(newDiv);
+ */
+const homeworkContainer = document.querySelector('#homework-container');
+
+/*
+ Функция должна создавать и возвращать новый div с классом draggable-div и случайными размерами/цветом/позицией
+ Функция должна только создавать элемент и задвать ему случайные размер/позицию/цвет
+ Функция НЕ должна добавлять элемент на страницу. На страницу элемент добавляется отдельно
+
+ Пример:
+ const newDiv = createDiv();
+ homeworkContainer.appendChild(newDiv);
+ */
+function createDiv() {
+ function getRandom(min, max) {
+ return Math.random() * (max - min) + min;
+ }
+
+ const newDiv = document.createElement('div');
+
+ newDiv.style.height = getRandom(1, 600) + 'px';
+ newDiv.style.width = getRandom(1, 600) + 'px';
+ newDiv.style.backgroundColor = '#' + Math.random().toString(16).slice(-6);
+ newDiv.style.position = 'absolute';
+ newDiv.style.left = getRandom(1, 600) + 'px';
+ newDiv.style.top = getRandom(1, 600) + 'px';
+ newDiv.classList.add('draggable-div');
+
+ return newDiv;
+}
+
+/*
+ Функция должна добавлять обработчики событий для перетаскивания элемента при помощи drag and drop
+
+ Пример:
+ const newDiv = createDiv();
+ homeworkContainer.appendChild(newDiv);
+ addListeners(newDiv);
+ */
+function addListeners(target) {
+ target.addEventListener('mousedown', (event) => {
+ let oX = event.pageX - target.getBoundingClientRect().left;
+ let oY = event.pageY - target.getBoundingClientRect().top;
+
+ let moveTo = (event) => {
+ target.style.left = event.pageX - oX + 'px';
+ target.style.top = event.pageY - oY + 'px';
+ };
+
+ let mouseUp = () => {
+ document.removeEventListener('mousemove', moveTo);
+ target.removeEventListener('mouseup', mouseUp);
+ };
+
+ document.addEventListener('mousemove', moveTo);
+ target.addEventListener('mouseup', mouseUp);
+ });
+}
+
+let addDivButton = homeworkContainer.querySelector('#addDiv');
+
+addDivButton.addEventListener('click', function() {
+ // создать новый div
+ const div = createDiv();
+
+ // добавить на страницу
+ homeworkContainer.appendChild(div);
+ // назначить обработчики событий мыши для реализации D&D
+ addListeners(div);
+ // можно не назначать обработчики событий каждому div в отдельности, а использовать делегирование
+ // или использовать HTML5 D&D - https://www.html5rocks.com/ru/tutorials/dnd/basics/
+});
+
+export {
+ createDiv
+};
diff --git a/src/index.js b/src/index.js
index e69de29..3ad23d2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -0,0 +1,94 @@
+/* ДЗ 5 - DOM Events */
+
+/*
+ Задание 1:
+
+ Функция должна добавлять обработчик fn события eventName к элементу target
+
+ Пример:
+ addListener('click', document.querySelector('a'), () => console.log('...')) // должна добавить указанный обработчик кликов на указанный элемент
+ */
+function addListener(eventName, target, fn) {
+ target.addEventListener(eventName, fn);
+}
+
+/*
+ Задание 2:
+
+ Функция должна удалять у элемента target обработчик fn события eventName
+
+ Пример:
+ removeListener('click', document.querySelector('a'), someHandler) // должна удалить указанный обработчик кликов на указанный элемент
+ */
+function removeListener(eventName, target, fn) {
+ target.removeEventListener(eventName, fn)
+}
+
+/*
+ Задание 3:
+
+ Функция должна добавить к элементу target такой обработчик на события eventName, чтобы он отменял действия по умолчанию
+
+ Пример:
+ skipDefault('click', document.querySelector('a')) // после вызова функции, клики на указанную ссылку не должны приводить к переходу на другую страницу
+ */
+function skipDefault(eventName, target) {
+ target.addEventListener(eventName, (e) => {
+ e.preventDefault();
+ })
+}
+
+/*
+ Задание 4:
+
+ Функция должна эмулировать событие click для элемента target
+
+ Пример:
+ emulateClick(document.querySelector('a')) // для указанного элемента должно быть сэмулировано события click
+ */
+function emulateClick(target) {
+ let click = new Event('click');
+
+ target.dispatchEvent(click);
+}
+
+/*
+ Задание 5:
+
+ Функция должна добавить такой обработчик кликов к элементу target,
+ который реагирует (вызывает fn) только на клики по элементам BUTTON внутри target
+
+ Пример:
+ delegate(document.body, () => console.log('кликнули на button')) // добавит такой обработчик кликов для body, который будет вызывать указанную функцию только если кликнули на кнопку (элемент с тегом button)
+ */
+function delegate(target, fn) {
+ target.addEventListener('click', e => {
+ e.target.tagName === 'BUTTON' ? fn() : null;
+ });
+}
+
+/*
+ Задание 6:
+
+ Функция должна добавить такой обработчик кликов к элементу target,
+ который сработает только один раз и удалится (перестанет срабатывать для последующих кликов по указанному элементу)
+
+ Пример:
+ once(document.querySelector('button'), () => console.log('обработчик выполнился!')) // добавит такой обработчик кликов для указанного элемента, который вызовется только один раз и затем удалится
+ */
+function once(target, fn) {
+ function remove() {
+ fn();
+ target.removeEventListener('click', remove);
+ }
+ target.addEventListener('click', remove);
+}
+
+export {
+ addListener,
+ removeListener,
+ skipDefault,
+ emulateClick,
+ delegate,
+ once
+};
diff --git a/test/dnd.js b/test/dnd.js
new file mode 100644
index 0000000..1c9460c
--- /dev/null
+++ b/test/dnd.js
@@ -0,0 +1,47 @@
+import assert from 'assert';
+let template = require('../dnd-content.hbs');
+
+describe('ДЗ 5.2 - Div D&D', () => {
+ let homeworkContainer = document.createElement('div');
+ let addDivButton;
+ let dndPage;
+
+ homeworkContainer.id = 'homework-container';
+ homeworkContainer.innerHTML = template();
+ document.body.appendChild(homeworkContainer);
+ dndPage = require('../src/dnd');
+
+ describe('Функциональное тестирование', () => {
+ describe('createDiv', () => {
+ it('должна создавать div с произвольными размерами/позицией/цветом', () => {
+ let result = dndPage.createDiv();
+
+ assert(result instanceof Element, 'не элемент');
+ assert.equal(result.tagName, 'DIV', 'имя тега не DIV');
+ assert.notEqual(result.style.backgroundColor || result.style.background, '', 'не указан цвет фона');
+ assert.notEqual(result.style.top, '', 'не указана позиция по оси Y');
+ assert.notEqual(result.style.left, '', 'не указана позиция по оси X');
+ assert.notEqual(result.style.width, '', 'не указана ширина');
+ assert.notEqual(result.style.height, '', 'не указана высота');
+ });
+ });
+ });
+
+ describe('Интеграционное тестирование', () => {
+ it('на старнице должна быть кнопка с id addDiv', () => {
+ addDivButton = homeworkContainer.querySelector('#addDiv');
+ assert(addDivButton instanceof Element, 'не элемент');
+ assert.equal(addDivButton.tagName, 'BUTTON', 'имя тега не BUTTON');
+ });
+
+ it('создавать div с классом draggable-div при клике на кнопку', () => {
+ let divsCount = homeworkContainer.querySelectorAll('.draggable-div').length;
+ let newDivsCount;
+
+ addDivButton.dispatchEvent(new MouseEvent('click', { view: window }));
+ newDivsCount = homeworkContainer.querySelectorAll('.draggable-div').length;
+
+ assert.equal(newDivsCount - divsCount, 1);
+ });
+ });
+});
diff --git a/test/index.js b/test/index.js
index 9d2a198..8fa5e00 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,7 +1,111 @@
import assert from 'assert';
+import {
+ addListener,
+ removeListener,
+ skipDefault,
+ emulateClick,
+ delegate,
+ once
+} from '../src/index';
-describe('Test', () => {
- it('should work', () => {
- assert(true == true);
+describe('ДЗ 5.1 - DOM Events', () => {
+ describe('addListener', () => {
+ it('должна добавлять обработчик событий элемента', () => {
+ let target = document.createElement('div');
+ let eventName = 'click';
+ let passed = false;
+ let fn = () => passed = true;
+
+ addListener(eventName, target, fn);
+
+ assert(!passed);
+ target.dispatchEvent(new CustomEvent(eventName));
+ assert(passed);
+ });
+ });
+
+ describe('removeListener', () => {
+ it('должна удалять обработчик событий элемента', () => {
+ let target = document.createElement('div');
+ let eventName = 'click';
+ let passed = false;
+ let fn = () => passed = true;
+
+ target.addEventListener(eventName, fn);
+
+ removeListener(eventName, target, fn);
+
+ target.dispatchEvent(new CustomEvent(eventName));
+ assert(!passed);
+ });
+ });
+
+ describe('skipDefault', () => {
+ it('должна добавлять такой обработчик, который предотвращает действие по умолчанию', () => {
+ let target = document.createElement('div');
+ let eventName = 'click';
+ let result;
+
+ skipDefault(eventName, target);
+
+ result = target.dispatchEvent(new CustomEvent(eventName, { cancelable: true }));
+ assert(!result);
+ });
+ });
+
+ describe('emulateClick', () => {
+ it('должна эмулировать клик по элементу', () => {
+ let target = document.createElement('div');
+ let eventName = 'click';
+ let passed = false;
+ let fn = () => passed = true;
+
+ target.addEventListener(eventName, fn);
+
+ emulateClick(target);
+
+ assert(passed);
+ });
+ });
+
+ describe('delegate', () => {
+ it('должна добавлять обработчик кликов, который реагирует только на клики по кнопкам', () => {
+ let target = document.createElement('div');
+ let eventName = 'click';
+ let passed = false;
+ let fn = () => passed = true;
+
+ target.innerHTML = '';
+
+ delegate(target, fn);
+
+ assert(!passed);
+ target.children[0].dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
+ assert(!passed);
+ target.children[1].dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
+ assert(!passed);
+ target.children[2].dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
+ assert(!passed);
+ target.children[3].dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
+ assert(passed);
+ });
+ });
+
+ describe('once', () => {
+ it('должна добавлять обработчик кликов, который сработает только один раз и удалится', () => {
+ let target = document.createElement('div');
+ let eventName = 'click';
+ let passed = 0;
+ let fn = () => passed++;
+
+ once(target, fn);
+
+ assert.equal(passed, 0);
+ target.dispatchEvent(new CustomEvent(eventName));
+ assert.equal(passed, 1);
+ target.dispatchEvent(new CustomEvent(eventName));
+ assert.equal(passed, 1);
+ target.dispatchEvent(new CustomEvent(eventName));
+ });
});
});
diff --git a/webpack.config.js b/webpack.config.js
index 275df68..42e45ca 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -14,7 +14,13 @@ rules.push({
});
module.exports = {
- entry: './src/index.js',
+ entry: {
+ main: './src/index.js',
+ dnd: './src/dnd.js'
+ },
+ devServer: {
+ index: 'dnd.html'
+ },
output: {
filename: '[name].[hash].js',
path: path.resolve('dist')
@@ -31,8 +37,15 @@ module.exports = {
}),
new ExtractTextPlugin('styles.css'),
new HtmlPlugin({
- title: 'Loft School sample project',
- template: 'index.hbs'
+ title: 'Main Homework',
+ template: 'main.hbs',
+ chunks: ['main']
+ }),
+ new HtmlPlugin({
+ title: 'Div Drag And Drop',
+ template: 'dnd.hbs',
+ filename: 'dnd.html',
+ chunks: ['dnd']
}),
new CleanWebpackPlugin(['dist'])
]