5x5 checker board controlled by keyboard with different events at boxes - javascript

So i have this project am working on which is to traverse through 5x5 checker board with different events occurring at different boxes(positions) in the board (e.g if the person is at [2,0] an event to reduce the grid to only 2x2 size occurs or perhaps the box at [4,0] changes color)
much appreciate the help.
(function() {
// refined add event cross browser
function addEvent(elem, event, fn) {
if (typeof elem === "string") {
elem = document.getElementById(elem);
}
// avoid memory overhead of new anonymous functions for every event handler that's installed
// by using local functions
function listenHandler(e) {
var ret = fn.apply(this, arguments);
if (ret === false) {
e.stopPropagation();
e.preventDefault();
}
return(ret);
}
function attachHandler() {
// set the this pointer same as addEventListener when fn is called
// and make sure the event is passed to the fn also so that works the same too
var ret = fn.call(elem, window.event);
if (ret === false) {
window.event.returnValue = false;
window.event.cancelBubble = true;
}
return(ret);
}
if (elem.addEventListener) {
elem.addEventListener(event, listenHandler, false);
} else {
elem.attachEvent("on" + event, attachHandler);
}
}
function addClass(elem, cls) {
var oldCls = elem.className;
if (oldCls) {
oldCls += " ";
}
elem.className = oldCls + cls;
}
function removeClass(elem, cls) {
var str = " " + elem.className + " ";
elem.className = str.replace(" " + cls + " ", " ").replace(/^\s+|\s+$/g, "");
}
function findItem(items, target) {
for (var i = 0; i < items.length; i++) {
if (items[i] === target) {
return(i);
}
}
return(-1);
}
var keys = {up: 38, down: 40, left: 37, right: 39};
var cards = document.getElementById("game-board").getElementsByClassName("card");
addEvent(document, "keydown", function(e) {
// get key press in cross browser way
var code = e.which || e.keyCode;
// number of items across
var width = 4;
var increment, index, newIndex, active;
switch(code) {
case keys.up:
increment = -width;
break;
case keys.down:
increment = width;
break;
case keys.left:
increment = -1;
break;
case keys.right:
increment = 1;
break;
default:
increment = 0;
break;
}
if (increment !== 0) {
active = document.getElementById("game-board").getElementsByClassName("active")[0];
index = findItem(cards, active);
newIndex = index + increment;
if (newIndex >= 0 && newIndex < cards.length) {
removeClass(active, "active");
addClass(cards[newIndex], "active");
}
// prevent default handling of up, down, left, right keys
return false;
}
});
})();
.active {
background: #00ebe4;
border:2px solid #93ff85;
}
*{margin: 0; padding:0;}
body{
font-family:arial;
background: #5f5f5f;
}
#plan{
margin:90px auto;
width:400px;
height:400px;
background: rgba(95, 95, 95, 0.84);
border-radius: 10px;
}
#game-board{
width:380px;
height:380px;
}
.card{
box-shadow: 0 2px 2px 1px #000;
border-radius: 5px;
height:70px;
width:70px;
display:inline-block;
margin-left:20px;
margin-top:20px;
background: #3a89be 14px 7px no-repeat;
}
<body>
<div align="center";
style="color: #d8d8d8;
margin-top: 50px" >
<H1></H1>
<h2>use Arrows to Navigate.</h2></div>
<div id="plan">
<div id="game-board">
<div class="card active" id="0"></div>
<div class="card" id="1"></div>
<div class="card" id="2"></div>
<div class="card" id="3"></div>
<div class="card" id="4"></div>
<div class="card" id="5"></div>
<div class="card" id="6"></div>
<div class="card" id="7"></div>
<div class="card" id="8"></div>
<div class="card" id="9"></div>
<div class="card" id="10"></div>
<div class="card" id="11"></div>
<div class="card" id="12"></div>
<div class="card" id="13"></div>
<div class="card" id="14"></div>
<div class="card" id="15"></div>
</div>
</div>
<!-- <div id="restart"><button id="reset-button"></button>reset</div> -->
</body>

This is in response to your statement/clarification:
"but what i dont know is how to keep track of these actions on
specific boxes, thanks"
Take a look at event listeners in javascript. I know you're using some above, but it looks like you may have copied from somewhere -- which is fine -- but it's in a way that makes them less then useful for this purpose. Event listeners are normally setup on specific elements on the page, using getElementById. Example:
HTML
<button id='button1'>Click Me!</button>
JS
document.getElementById('button1').addEventListener("click", myFunction, true);
myFunction = function(){
//here's where you change the color, or size, or make a popup window, etc.
};
Clicking on that particular button (which can also be any other HTML element) will execute the function.

Related

How to save new background color via `colorpicker` to `localStorage`?

I know there's a lot of similar questions.. I've tried them but I really can't incorporate it to my project.
I'm trying to save to localStorage the new background-color selected using the native colorpicker.
I'm almost there but I just can't figure out how to make it work.
Please see my code so far:
function changeBgColor(color) {
if (color) window.localStorage.setItem('bgColor', color);
else if (!(color = window.localStorage.getItem('bgColor'))) return;
document.getElementById('colorpicker').addEventListener('input', function() {
var elements = document.getElementsByClassName("card-bg")
for (var i = 0; i < elements.length; i++) {
elements[i].style.background = this.value;
}
})
}
window.addEventListener('DOMContentLoaded', () => changeBgColor());
.card1 {
width: 200px;
height: 200px;
background-color: #222;
margin: 10px 0 0 0;
}
<div class="card1 card-bg">Set A</div>
<div class="card1 card-bg">Set A</div>
<div class="card1 card-bg">Set A</div>
<br>
<input type="color" id="colorpicker" onselect="changeBgColor();">
The important feature for me are:
To change background color using input="color"
Use class selector since I have multiple divs I want to target with the same input
Save the value to localStorage
That's all really. I just need to figure out the part where the value gets saved to localStorage.
After I make this work, I will need to replicate it for a different set of divs..
Thank you in advance for any help.
If check had = assignment instead of == comparison
Here is a working refactored snippet:
let pretend_local_storage = null
function setupBackgroundColorChange() {
const elements = document.querySelectorAll(".card-bg")
document.getElementById('colorpicker').addEventListener('input', function(e) {
color = e.target.value
if (color === pretend_local_storage) return
pretend_local_storage = color
elements.forEach((element) => element.style.background = color)
})
}
window.onDOMContentLoaded = setupBackgroundColorChange();
.card1 {
width: 200px;
height: 200px;
background-color: #222;
margin: 10px 0 0 0;
}
<div class="card1 card-bg">Set A</div>
<div class="card1 card-bg">Set A</div>
<div class="card1 card-bg">Set A</div>
<br>
<input type="color" id="colorpicker" onselect="changeBgColor();">
Local Storage example:
localStorage.getItem('bgColor', null)
function setupBackgroundColorChange() {
const elements = document.querySelectorAll(".card-bg")
setColor(elements, localStorage.getItem('bgColor', '#000'))
document.getElementById('colorpicker').addEventListener('input', function (e) {
color = e.target.value
if (color === localStorage.getItem('bgColor')) return
localStorage.setItem('bgColor', color)
setColor(elements, color)
})
}
function setColor(elements, color) {
elements.forEach((element) => element.style.background = color)
}
window.onDOMContentLoaded = setupBackgroundColorChange();
First of all i would use onchange trigger. To be honest, you dont need any condition inside the function. you can set the color to localStorage and that is it.
/* this lines check if already set color to localStorage */
if (window.localStorage.getItem('bgColor')) {
var elements = document.getElementsByClassName("card-bg")
for (var i = 0; i < elements.length; i++) {
elements[i].style.background = this.value;
}
}
function changeBgColor() {
let color = document.getElementById('colorpicker').value;
const bg_curr = localStorage.getItem('bgColor');
console.log(color, bg_curr);
localStorage.setItem('bgColor', color);
document.getElementById('colorpicker').addEventListener('input', function() {
var elements = document.getElementsByClassName("card-bg")
for (var i = 0; i < elements.length; i++) {
elements[i].style.background = color;
}
})
}
window.addEventListener('DOMContentLoaded', () => changeBgColor());
.card1 {
width: 200px;
height: 200px;
background-color: #222;
margin: 10px 0 0 0;
}
<input type="color" id="colorpicker" onchange="changeBgColor();">
<div class="card1 card-bg">Set A</div>
<div class="card1 card-bg">Set A</div>
<div class="card1 card-bg">Set A</div>
<br>

How to remove highlighted multiple html contenteditable elements?

How can I highlight multiple HTML elements and remove them by pressing the Backspace key? WordPress block editor and Editor.js has this feature. Users can select/ Highlight multiple block and remove them by pressing the Backspace key.
From this code below, If I highlight by selecting(mouse) not by clicking the .block-1, .block-2, .block-3 and press Backspace key then it should delete these elements.
[NOTE]
Every block should have contenteditable attribute.
Please, go to Editor.js first for demo what I exact want.
<div class="block-1" contenteditable="true"> 1st Block </div>
<div class="block-2" contenteditable="true"> 2nd Block </div>
<div class="block-3" contenteditable="true"> 3rd Block </div>
<div class="block-4" contenteditable="true"> 4th Block </div>
We need 2 listeners:
1 - on mouseup, which catches selected text, and use TreeWalker to get all the highlighted elements and toggle selected class on .blocks.
2 - on keyup, which will catch backspace
Edit:
Used This and improved the answer .
$(document).on({
'keyup': function(e) {
if (e.which == 8)
$('div.block.selected').remove();
},
'mouseup': getSelectedElementTags
});
function rangeIntersectsNode(range, node) {
var nodeRange;
if (range.intersectsNode) {
return range.intersectsNode(node);
} else {
nodeRange = node.ownerDocument.createRange();
try {
nodeRange.selectNode(node);
} catch (e) {
nodeRange.selectNodeContents(node);
}
return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
}
}
function getSelectedElementTags() {
var win = window;
var range, sel, elmlist, treeWalker, containerElement;
sel = win.getSelection();
if (sel.toString().length == 0) return
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0);
}
if (range) {
containerElement = range.commonAncestorContainer;
if (containerElement.nodeType != 1) {
containerElement = containerElement.parentNode;
}
treeWalker = win.document.createTreeWalker(
containerElement,
NodeFilter.SHOW_ELEMENT,
function(node) {
return rangeIntersectsNode(range, node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
},
false
);
elmlist = [treeWalker.currentNode];
while (treeWalker.nextNode()) {
elmlist.push(treeWalker.currentNode);
}
elmlist.forEach(function(e) {
if ($(e).hasClass('block')) {
$(e).toggleClass('selected');
}
});
sel.empty()
}
}
div.block.selected {
background-color: #ddf;
}
div.block {
margin: 24px;
border-bottom: 1px solid #ddd;
font-size: 13px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container" contenteditable="true">
<div class="block block-1" contenteditable="true"> 1st Block</div>
<div class="block block-2" contenteditable="true"> 2nd Block </div>
<div class="block block-3" contenteditable="true"> 3rd Block</div>
<div class="block block-4"> 4th Block </div>
</div>
The CSS and the contenteditable attribute are not necessary. Here is the code
var targeted;
document.getElementById('container').addEventListener('click', function(event) {
console.log(event.target); // You can see here the targeted element
if(event.target.id !== 'container') {
targeted = event.target;
} else {
targeted = undefined;
};
});
document.addEventListener('keydown', function(event) {
if(event.keyCode === 8 && targeted) {
targeted.parentNode.removeChild(targeted);
};
});
#container {
padding: 20px;
background-color: black;
}
#container div {
margin-bottom: 5px;
height: 50px;
width: 200px;
background-color: white;
}
<div id="container">
<div contenteditable="true">1</div>
<div contenteditable="true">2</div>
<div contenteditable="true">3</div>
<div contenteditable="true">4</div>
<div contenteditable="true">5</div>
</div>

Change classes only for the child clicked within the parent using JavaScript

I have two div's within a parent div. I need to change the classes for the child div which I clicked. For that I am writing a method to check which child was clicked and respectively I am trying to hide the other child div.
But I am not able to add classes or remove classes since the index is showing always as undefined. I am feeling there is some problem with the return statement.
function changeClass() {
const list = document.getElementById('my_div').children;
const indx = this.getIndexOfParent(list);
for (let i = 0; i < list.length; i++) {
if (indx === 0) {
list[indx + 1].classList.add("d-none d-sm-block");
list[indx].classList.remove("col-6 d-none d-sm-block");
} else if (indx === 1) {
list[indx - 1].classList.add("d-none d-sm-block");
list[indx].classList.remove("col-6 d-none d-sm-block");
}
list[indx].classList.add("d-xs-block");
}
}
function getIndexOfParent(child_list) {
for (var i = 0, len = child_list.length; i < len; i++) {
((index) => {
child_list[i].onclick = () => {
return index;
};
})(i);
}
}
.row {
background: #f8f9fa;
margin-top: 20px;
}
.row > div {
border: solid 1px black;
padding: 10px;
}
<div class="container">
<div class="row">
<div onclick="changeClass()" class="col-md-6 col-6">
child-div-1
</div>
<div onclick="changeClass()" class="col-md-6 col-6">
child-div-2
</div>
</div>
</div>
All I want is that, when I click on child-div-1 it should hide child-div-2 and vice versa only for small screens (which is why I am handling it by col-6 and d-xs-block classes)
Can anyone help me to solve the below problem.
You have added onclick within the for loop. Instead add the class to the clicked child div and remove the class from it's sibling div.
document.querySelectorAll('div.row > div')
.forEach((div) => {
div.addEventListener('click', function({
target
}) {
target.classList.add('d-none', 'd-sm-block');
const sibDiv = Array.prototype.filter.call(target.parentNode.children, div => div != target)[0];
sibDiv.classList.remove('col-6', 'd-none', 'd-sm-block');
});
});
.row {
background: #f8f9fa;
margin-top: 20px;
}
.row>div {
border: solid 1px black;
padding: 10px;
}
<div class="container">
<div class="row">
<div class="col-md-6 col-6">
child-div-1
</div>
<div class="col-md-6 col-6">
child-div-2
</div>
</div>
</div>
-- Edit --
The return statement will return the value of index to the callback function, you also need to add return to the callback function, so whatever result the callback function get will return to the function getIndexOfParent.
function getIndex() {
let i = 0;
((index) => { // No return, logs undefined
return index;
})(i);
}
console.log(getIndex());
function getIndex() {
let i = 0;
return ((index) => { // with return
return index;
})(i);
}
console.log(getIndex());
I made a pen for solving this problem.
Check the pen here
the solution is easy you simply have to write this line
e.stopPropagation();
this will stop the event from triggering on parent divs
basically what you are describing is called event bubbling.
you can read about it more on medium

Switch classes on click next or back

I'm trying to setup multiple-step form in which the first step is visible by default and rest of the steps are hidden with class "hide". I'd like to switch the class with Next and Back button so only one step is visible at a time. Could you please help with this (Already spent an hour on this)
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
$('.next').click(function(){
$('div:not(.hide)').next().removeClass('hide');
$('.hide').prev().removeClass('hide')
})
Try combining the 2 actions into one, like so:
$('.next').click(function(){
$('.steps div:not(.hide)').addClass('hide').next().removeClass('hide');
})
That way, you add the .hide class on your current div and then remove it on the next one.
You can use something similar for the Back button, by replacing .next() with .previous()
$('.next').click(function() {
// find the div that is not hidden
var $current = $('.steps div:not(.hide)');
// only perform logic if there is a proceeding div
if ($current.next().length) {
// show the next div
$current.next().removeClass('hide');
// hide the old current div
$current.addClass('hide')
}
});
$('.back').click(function() {
// find the div that is not hidden
var $current = $('.steps div:not(.hide)');
// only perform logic if there is a preceeding div
if ($current.prev().length) {
// show the previous div
$current.prev().removeClass('hide');
// hide the old current div
$current.addClass('hide')
}
});
.hide { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
You can add a current step variable to track the currently displayed step and two css for styling and showing your content.
jQuery(function($) {
let currentstep = 1;
let maxsteps = 4;
function showstep(step) {
let step_c = '.step' + step;
for (i = 1; i <= maxsteps; i++) {
var step_selector = '.step' + i;
$(step_selector).removeClass('show');
$(step_selector).addClass('hide');
}
$(step_c).removeClass('hide');
$(step_c).addClass('show');
};
$('.next').click(function() {
currentstep = currentstep + 1;
currentstep = (currentstep % (maxsteps + 1));
if (currentstep == 0) currentstep = 1;
showstep(currentstep);
});
$('.back').click(function() {
currentstep = currentstep - 1;
currentstep = (currentstep % (maxsteps + 1));
if (currentstep == 0) currentstep = 4;
showstep(currentstep);
});
});
.hide {
display: none;
}
.show {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1 show">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
I converted Taplar's answer to a jQuery plugin.
You are essentially navigating left or right by one, using the previous and next functions. These functions navigate through the sibling elements.
(function() {
$.fn.moveRight = function(className) {
var $curr = this.find('div:not(.' + className + ')');
if ($curr.next().length) $curr.next().removeClass(className);
else this.find('div:first-child').removeClass(className);
$curr.addClass(className);
return this;
};
$.fn.moveLeft = function(className) {
var $curr = this.find('div:not(.' + className + ')');
if ($curr.prev().length) $curr.prev().removeClass(className);
else this.find('div:last-child').removeClass(className);
$curr.addClass(className);
return this;
};
})(jQuery);
$('.next').on('click', (e) => $('.steps').moveRight('hide'));
$('.back').on('click', (e) => $('.steps').moveLeft('hide'));
.hide {
display: none;
}
.nav {
width: 260px;
text-align: center;
}
.nav .nav-btn::selection { background: transparent; }
.nav .nav-btn::-moz-selection { background: transparent; }
.nav .nav-btn {
display: inline-block;
cursor: pointer;
}
.steps {
width: 260px;
height: 165px;
border: thin solid black;
text-align: center;
line-height: 165px;
font-size: 3em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="nav">
<div class="nav-btn back">[ << Back ]</div>
<div class="nav-btn next">[ Next >> ]</div>
</div>

Connect two div by line with jquery function

I have two groups of quiz.
The first group is correct but the second group is not showing the line between two points.
The users click the point on the left and click the point on the right, then JavaScript creates a "canvas" line from the first element to the second element.
(I apologise for my english, it's my second language)
(function($) {
$.fn.connect = function(param) {
var _canvas;
var _ctx;
var _lines = new Array(); //This array will store all lines (option)
var _me = this;
var _parent = param || document;
var _lengthLines = $(_parent + ' .group1 .node').length;
var _selectFirst = null;
//Initialize Canvas object
_canvas = $('<canvas/>')
.attr('width', $(_me).width())
.attr('height', $(_me).height())
.css('position', 'absolute');
$(_parent).prepend(_canvas);
//$(_canvas).insertBefore(_parent);
this.drawLine = function(option) {
//It will push line to array.
_lines.push(option);
this.connect(option);
};
this.drawAllLine = function(option) {
/*Mandatory Fields------------------
left_selector = '.class',
data_attribute = 'data-right',
*/
if (option.left_selector != '' && typeof option.left_selector !== 'undefined' && $(option.left_selector).length > 0) {
$(option.left_selector).each(function(index) {
var option2 = new Object();
$.extend(option2, option);
option2.left_node = $(this).attr('id');
option2.right_node = $(this).data(option.data_attribute);
if (option2.right_node != '' && typeof option2.right_node !== 'undefined') {
_me.drawLine(option2);
}
});
}
};
//This Function is used to connect two different div with a dotted line.
this.connect = function(option) {
_ctx = _canvas[0].getContext('2d');
//
_ctx.beginPath();
try {
var _color;
var _dash;
var _left = new Object(); //This will store _left elements offset
var _right = new Object(); //This will store _right elements offset
var _error = (option.error == 'show') || false;
/*
option = {
left_node - Left Element by ID - Mandatory
right_node - Right Element ID - Mandatory
status - accepted, rejected, modified, (none) - Optional
style - (dashed), solid, dotted - Optional
horizantal_gap - (0), Horizantal Gap from original point
error - show, (hide) - To show error or not
width - (2) - Width of the line
}
*/
if (option.left_node != '' && typeof option.left_node !== 'undefined' && option.right_node != '' && typeof option.right_node !== 'undefined' && $(option.left_node).length > 0 && $(option.right_node).length > 0) {
//To decide colour of the line
switch (option.status) {
case 'accepted':
_color = '#0969a2';
break;
case 'rejected':
_color = '#e7005d';
break;
case 'modified':
_color = '#bfb230';
break;
case 'none':
_color = 'grey';
break;
default:
_color = 'grey';
break;
}
//To decide style of the line. dotted or solid
switch (option.style) {
case 'dashed':
_dash = [4, 2];
break;
case 'solid':
_dash = [0, 0];
break;
case 'dotted':
_dash = [4, 2];
break;
default:
_dash = [4, 2];
break;
}
/*
console.log($(option.left_node));
$(option.left_node)
$(option.right_node).data('connect',true);
*/
//If left_node is actually right side, following code will switch elements.
$(option.right_node).each(function(index, value) {
_left_node = $(option.left_node);
_right_node = $(value);
_left_node.attr('data-connect', true);
_right_node.attr('data-connect', true);
if (_left_node.offset().left >= _right_node.offset().left) {
_tmp = _left_node
_left_node = _right_node
_right_node = _tmp;
}
//Get Left point and Right Point
_left.x = _left_node.offset().left + _left_node.outerWidth();
_left.y = _left_node.offset().top + (_left_node.outerHeight() / 2);
_right.x = _right_node.offset().left;
_right.y = _right_node.offset().top + (_right_node.outerHeight() / 2);
//Create a group
//var g = _canvas.group({strokeWidth: 2, strokeDashArray:_dash});
//Draw Line
var _gap = option.horizantal_gap || 0;
_ctx.moveTo(_left.x, _left.y);
if (_gap != 0) {
_ctx.lineTo(_left.x + _gap, _left.y);
_ctx.lineTo(_right.x - _gap, _right.y);
}
_ctx.lineTo(_right.x, _right.y);
if (!_ctx.setLineDash) {
_ctx.setLineDash = function() {}
} else {
_ctx.setLineDash(_dash);
}
_ctx.lineWidth = option.width || 2;
_ctx.strokeStyle = _color;
_ctx.stroke();
});
//option.resize = option.resize || false;
} else {
if (_error) alert('Mandatory Fields are missing or incorrect');
}
} catch (err) {
if (_error) alert('Mandatory Fields are missing or incorrect');
}
//console.log(_canvas);
};
//It will redraw all line when screen resizes
$(window).resize(function() {
console.log(_me);
_me.redrawLines();
});
$(_parent + ' .group1 .node span').click(function() {
//console.log($(this).attr('data-connect'));
//[data-use="false"]
_this = this;
if ($(_this).attr('data-connect') != 'true' && $(_this).attr('data-use') == 'false') {
$(_parent + ' .group1 .node span').attr('data-use', 'false');
$(_this).attr('data-use', 'true');
_selectFirst = _this;
} else if ($(_this).attr('data-connect') == 'true') {
//console.log($(this).attr('data-id'));
//console.log(entry);
_lines.forEach(function(entry, index) {
if ($(_this).attr('data-id') == entry.id_left) {
$(entry.left_node).attr('data-use', 'false').attr('data-connect', 'false')
$(entry.right_node).attr('data-use', 'false').attr('data-connect', 'false')
_lines.splice(index, 1)
}
});
_me.redrawLines();
}
});
$(_parent + ' .group2 .node span[data-use="false"]').click(function() {
if ($(_parent + ' .group1 .node span[data-use="true"]').length == 1 && _selectFirst != null) {
if ($(this).attr('data-connect') != 'true') {
_me.drawLine({
id_left: $(_selectFirst).attr('data-id'),
id_right: $(this).attr('data-id'),
left_node: _selectFirst,
right_node: this,
horizantal_gap: 10,
error: 'show',
width: 1,
status: 'accepted'
});
$(_selectFirst).attr('data-use', 'false');
$(_selectFirst).attr('data-connect', 'true');
$(this).attr('data-use', 'false');
$(this).attr('data-connect', 'true');
}
}
});
this.redrawLines = function() {
_ctx.clearRect(0, 0, $(_me).width(), $(_me).height());
_lines.forEach(function(entry) {
entry.resize = true;
_me.connect(entry);
});
};
return this;
};
}(jQuery));
.clearfix {
clear: both;
}
body {
padding: 0px;
margin: 0px;
}
.nodes {
width: 500px
}
.node {
width: 100px;
background: #ddd;
color: #fff;
margin-bottom: 10px;
}
.group1 span {
background: #666;
border-radius: 50%;
width: 10px;
height: 10px;
margin-top: 5px;
}
.group2 span {
border: 1px solid #666;
border-radius: 50%;
width: 10px;
height: 10px;
margin-top: 5px;
}
.node span:hover {
background: #ff0000;
cursor: pointer;
}
.group1 {
float: left;
}
.group2 {
float: right;
}
.group2 .node span {
float: left;
position: relative;
left: -15px;
}
.group1 .node span {
float: right;
position: relative;
right: -15px;
}
.node span[data-connect=true] {
background: #ff00ff !important;
}
.node span[data-use=true] {
background: #ff0000 !important;
}
<div id="parentNodes_11">
<div class="nodes">
<div class="group1">
<div class="node">1<span class="node1" data-connect="false" data-id="0" data-use="false"></span></div>
<div class="node">2 <span class="node2" data-connect="false" data-id="1" data-use="false"></span></div>
<div class="node">3 <span class="node3" data-connect="false" data-id="2" data-use="false"></span></div>
</div>
<div class="group2">
<div class="node">A <span class="node4" data-connect="false" data-id="0" data-use="false"></span></div>
<div class="node">B <span class="node5" data-connect="false" data-id="1" data-use="false"></span></div>
<div class="node">C <span class="node6" data-connect="false" data-id="2" data-use="false"></span></div>
</div>
<div class="clearfix"></div>
</div>
</div>
<br>
<div id="parentNodes_12">
<div class="nodes">
<div class="group1">
<div class="node">1<span class="node1" data-connect="false" data-id="0" data-use="false"></span></div>
<div class="node">2 <span class="node2" data-connect="false" data-id="1" data-use="false"></span></div>
<div class="node">3 <span class="node3" data-connect="false" data-id="2" data-use="false"></span></div>
</div>
<div class="group2">
<div class="node">A <span class="node4" data-connect="false" data-id="0" data-use="false"></span></div>
<div class="node">B <span class="node5" data-connect="false" data-id="1" data-use="false"></span></div>
<div class="node">C <span class="node6" data-connect="false" data-id="2" data-use="false"></span></div>
</div>
<div class="clearfix"></div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#parentNodes_11 .nodes').connect('#parentNodes_11');
$('#parentNodes_12 .nodes').connect('#parentNodes_12');
});
</script>
This is due to the use of absolute coordinates for lines instead of relative ones, so your lines doesn't fit on the canvas.
You can just do the parent offset adjustment and it will work like this:
(function($) {
$.fn.connect = function(param) {
var _canvas;
var _ctx;
var _lines = new Array(); //This array will store all lines (option)
var _me = this;
var _parent = param || document;
var _lengthLines = $(_parent + ' .group1 .node').length;
var _selectFirst = null;
//Initialize Canvas object
_canvas = $('<canvas/>')
.attr('width', $(_me).width())
.attr('height', $(_me).height())
.css('position', 'absolute');
$(_parent).prepend(_canvas);
//$(_canvas).insertBefore(_parent);
this.drawLine = function(option) {
//It will push line to array.
_lines.push(option);
this.connect(option);
};
this.drawAllLine = function(option) {
/*Mandatory Fields------------------
left_selector = '.class',
data_attribute = 'data-right',
*/
if (option.left_selector != '' && typeof option.left_selector !== 'undefined' && $(option.left_selector).length > 0) {
$(option.left_selector).each(function(index) {
var option2 = new Object();
$.extend(option2, option);
option2.left_node = $(this).attr('id');
option2.right_node = $(this).data(option.data_attribute);
if (option2.right_node != '' && typeof option2.right_node !== 'undefined') {
_me.drawLine(option2);
}
});
}
};
//This Function is used to connect two different div with a dotted line.
this.connect = function(option) {
_ctx = _canvas[0].getContext('2d');
//
_ctx.beginPath();
try {
var _color;
var _dash;
var _left = new Object(); //This will store _left elements offset
var _right = new Object(); //This will store _right elements offset
var _error = (option.error == 'show') || false;
/*
option = {
left_node - Left Element by ID - Mandatory
right_node - Right Element ID - Mandatory
status - accepted, rejected, modified, (none) - Optional
style - (dashed), solid, dotted - Optional
horizantal_gap - (0), Horizantal Gap from original point
error - show, (hide) - To show error or not
width - (2) - Width of the line
}
*/
if (option.left_node != '' && typeof option.left_node !== 'undefined' && option.right_node != '' && typeof option.right_node !== 'undefined' && $(option.left_node).length > 0 && $(option.right_node).length > 0) {
//To decide colour of the line
switch (option.status) {
case 'accepted':
_color = '#0969a2';
break;
case 'rejected':
_color = '#e7005d';
break;
case 'modified':
_color = '#bfb230';
break;
case 'none':
_color = 'grey';
break;
default:
_color = 'grey';
break;
}
//To decide style of the line. dotted or solid
switch (option.style) {
case 'dashed':
_dash = [4, 2];
break;
case 'solid':
_dash = [0, 0];
break;
case 'dotted':
_dash = [4, 2];
break;
default:
_dash = [4, 2];
break;
}
/*
console.log($(option.left_node));
$(option.left_node)
$(option.right_node).data('connect',true);
*/
//If left_node is actually right side, following code will switch elements.
$(option.right_node).each(function(index, value) {
_left_node = $(option.left_node);
_right_node = $(value);
_left_node.attr('data-connect', true);
_right_node.attr('data-connect', true);
if (_left_node.offset().left >= _right_node.offset().left) {
_tmp = _left_node
_left_node = _right_node
_right_node = _tmp;
}
//Get Left point and Right Point
_left.x = _left_node.offset().left + _left_node.outerWidth();
_left.y = _left_node.offset().top + (_left_node.outerHeight() / 2) - _left_node.parents('.nodes').offset().top;
_right.x = _right_node.offset().left;
_right.y = _right_node.offset().top + (_right_node.outerHeight() / 2) - _right_node.parents('.nodes').offset().top;
//Create a group
//var g = _canvas.group({strokeWidth: 2, strokeDashArray:_dash});
//Draw Line
var _gap = option.horizantal_gap || 0;
_ctx.moveTo(_left.x, _left.y);
if (_gap != 0) {
_ctx.lineTo(_left.x + _gap, _left.y);
_ctx.lineTo(_right.x - _gap, _right.y);
}
_ctx.lineTo(_right.x, _right.y);
if (!_ctx.setLineDash) {
_ctx.setLineDash = function() {}
} else {
_ctx.setLineDash(_dash);
}
_ctx.lineWidth = option.width || 2;
_ctx.strokeStyle = _color;
_ctx.stroke();
});
//option.resize = option.resize || false;
} else {
if (_error) alert('Mandatory Fields are missing or incorrect');
}
} catch (err) {
if (_error) alert('Mandatory Fields are missing or incorrect');
}
//console.log(_canvas);
};
//It will redraw all line when screen resizes
$(window).resize(function() {
console.log(_me);
_me.redrawLines();
});
$(_parent + ' .group1 .node span').click(function() {
//console.log($(this).attr('data-connect'));
//[data-use="false"]
_this = this;
if ($(_this).attr('data-connect') != 'true' && $(_this).attr('data-use') == 'false') {
$(_parent + ' .group1 .node span').attr('data-use', 'false');
$(_this).attr('data-use', 'true');
_selectFirst = _this;
} else if ($(_this).attr('data-connect') == 'true') {
//console.log($(this).attr('data-id'));
//console.log(entry);
_lines.forEach(function(entry, index) {
if ($(_this).attr('data-id') == entry.id_left) {
$(entry.left_node).attr('data-use', 'false').attr('data-connect', 'false')
$(entry.right_node).attr('data-use', 'false').attr('data-connect', 'false')
_lines.splice(index, 1)
}
});
_me.redrawLines();
}
});
$(_parent + ' .group2 .node span[data-use="false"]').click(function() {
if ($(_parent + ' .group1 .node span[data-use="true"]').length == 1 && _selectFirst != null) {
if ($(this).attr('data-connect') != 'true') {
_me.drawLine({
id_left: $(_selectFirst).attr('data-id'),
id_right: $(this).attr('data-id'),
left_node: _selectFirst,
right_node: this,
horizantal_gap: 10,
error: 'show',
width: 1,
status: 'accepted'
});
$(_selectFirst).attr('data-use', 'false');
$(_selectFirst).attr('data-connect', 'true');
$(this).attr('data-use', 'false');
$(this).attr('data-connect', 'true');
}
}
});
this.redrawLines = function() {
_ctx.clearRect(0, 0, $(_me).width(), $(_me).height());
_lines.forEach(function(entry) {
entry.resize = true;
_me.connect(entry);
});
};
return this;
};
}(jQuery));
.clearfix {
clear: both;
}
body {
padding: 0px;
margin: 0px;
}
.nodes {
width: 500px
}
.node {
width: 100px;
background: #ddd;
color: #fff;
margin-bottom: 10px;
}
.group1 span {
background: #666;
border-radius: 50%;
width: 10px;
height: 10px;
margin-top: 5px;
}
.group2 span {
border: 1px solid #666;
border-radius: 50%;
width: 10px;
height: 10px;
margin-top: 5px;
}
.node span:hover {
background: #ff0000;
cursor: pointer;
}
.group1 {
float: left;
}
.group2 {
float: right;
}
.group2 .node span {
float: left;
position: relative;
left: -15px;
}
.group1 .node span {
float: right;
position: relative;
right: -15px;
}
.node span[data-connect=true] {
background: #ff00ff !important;
}
.node span[data-use=true] {
background: #ff0000 !important;
}
<div id="parentNodes_11">
<div class="nodes">
<div class="group1">
<div class="node">1<span class="node1" data-connect="false" data-id="0" data-use="false"></span></div>
<div class="node">2 <span class="node2" data-connect="false" data-id="1" data-use="false"></span></div>
<div class="node">3 <span class="node3" data-connect="false" data-id="2" data-use="false"></span></div>
</div>
<div class="group2">
<div class="node">A <span class="node4" data-connect="false" data-id="0" data-use="false"></span></div>
<div class="node">B <span class="node5" data-connect="false" data-id="1" data-use="false"></span></div>
<div class="node">C <span class="node6" data-connect="false" data-id="2" data-use="false"></span></div>
</div>
<div class="clearfix"></div>
</div>
</div>
<br>
<div id="parentNodes_12">
<div class="nodes">
<div class="group1">
<div class="node">1<span class="node1" data-connect="false" data-id="0" data-use="false"></span></div>
<div class="node">2 <span class="node2" data-connect="false" data-id="1" data-use="false"></span></div>
<div class="node">3 <span class="node3" data-connect="false" data-id="2" data-use="false"></span></div>
</div>
<div class="group2">
<div class="node">A <span class="node4" data-connect="false" data-id="0" data-use="false"></span></div>
<div class="node">B <span class="node5" data-connect="false" data-id="1" data-use="false"></span></div>
<div class="node">C <span class="node6" data-connect="false" data-id="2" data-use="false"></span></div>
</div>
<div class="clearfix"></div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#parentNodes_11 .nodes').connect('#parentNodes_11');
$('#parentNodes_12 .nodes').connect('#parentNodes_12');
});
</script>

Categories