Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dnd-content.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<button id="addDiv">добавить div</button>
12 changes: 12 additions & 0 deletions dnd.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{htmlWebpackPlugin.options.title}}</title>
</head>
<body>
<div id="homework-container">
{{> "./dnd-content.hbs"}}
</div>
</body>
</html>
91 changes: 91 additions & 0 deletions src/dnd.js
Original file line number Diff line number Diff line change
@@ -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
};
94 changes: 94 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -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
};
47 changes: 47 additions & 0 deletions test/dnd.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
});
110 changes: 107 additions & 3 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -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 = '<div></div><a href="#"></a><p></p><button></button>';

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));
});
});
});
19 changes: 16 additions & 3 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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'])
]
Expand Down