7

I was created 2 lines between 2 DIVs once user click the DIVs. Now I got problem on how to reset the unwanted line if I want to change my answer.

You may see my current code for your references:

var lastSelection;

// Add click listener for answer-container
function listenToClick() {
    var rows = document.querySelectorAll('.row'),
        row;
    var cols, col;

    for (row = 0; row < rows.length; row++) {
        cols = rows[row].children;

        for (col = 0; col < cols.length; col++) {
            // Bind information about which answer is this,
            // so we can prevent from connecting two answers on
            // same column.
            cols[col].addEventListener('click', selectAnswer.bind({
                row: row,
                col: col,
                element: cols[col]
            }));
        }
    }
}

// This is fired when a answer-container is clicked.
function selectAnswer(event) {
    if (lastSelection) {
        lastSelection.element.classList.remove('selected');
    }

    if (!lastSelection || lastSelection.col === this.col) {
        lastSelection = this;
        this.element.classList.add('selected');
    } else {
        drawLine(getPoint(this.element), getPoint(lastSelection.element));
        lastSelection = null;
    }
}

function getPoint(answerElement) {
    var roundPointer = answerElement.lastElementChild;

    return {
        y: answerElement.offsetTop + roundPointer.offsetTop + roundPointer.offsetHeight / 2,
        x: answerElement.offsetLeft + roundPointer.offsetLeft + roundPointer.offsetWidth / 2
    };
}

function drawLine(p1, p2) {
    var canvas = document.getElementById("connection-canvas");
    var ctx = canvas.getContext("2d");

    ctx.beginPath();
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
    ctx.stroke();
}

function resizeCanvas() {
    var canvas = document.getElementById("connection-canvas");
    var ctx = canvas.getContext("2d");

    ctx.canvas.width  = window.innerWidth;
    ctx.canvas.height = window.innerHeight;
}

listenToClick();
resizeCanvas();
.padding-answer-line-mapping
{
    padding-bottom:8px;
}

.answer-container
{
    width:40px;
    height:40px;
    background-color:#ccc;
    border:1px solid #ccc;
    margin:2px;
    float:left;
    text-align:center;
    padding-top:8px;
    cursor:pointer;
    position:relative;
}

.answer-container:hover, .answer-container:focus, .answer-container:active
{
    background-color: #0076e9;
    color: white;
    border: 1px solid #0076e9;
}

.round-pointer-right
{
    position: absolute;
    background-color:#ccc;
    cursor:pointer;
    width:10px;
    height:10px;
    border-radius: 50%;
    right:0px;
    top:14px;
    margin-right:-20px;
}

.round-pointer-left
{
    position: absolute;
    background-color:#ccc;
    cursor:pointer;
    width:10px;
    height:10px;
    border-radius: 50%;
    left:0px;
    top:14px;
    margin-left:-20px;
}

.selected {
    background-color: red;
}
<link href="//code.ionicframework.com/1.3.1/css/ionic.css" rel="stylesheet"/>
Match the following items.

 <canvas id="connection-canvas" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"></canvas>
<div class="row padding-answer-line-mapping">
    <div class="col answer-container">
        One
        <div class="round-pointer-right"></div>
    </div>
    <div class="col">
        
    </div>
    <div class="col answer-container">
        2
        <div class="round-pointer-left"></div>
    </div>
</div>
<div class="row padding-answer-line-mapping">
    <div class="col answer-container">
        Two
        <div class="round-pointer-right"></div>
    </div>
    <div class="col">
        
    </div>
    <div class="col answer-container">
        1
        <div class="round-pointer-left"></div>
    </div>
</div>
Nere
  • 4,097
  • 5
  • 31
  • 71
  • Didnt look too much into the code but in theory, just save a ref for each canvas and deleted in case the origin cell is the same.. – Roberto14 Oct 14 '16 at 09:10
  • If you can show me some codes then its better (^_^) – Nere Oct 14 '16 at 09:11
  • @Imran : I have made some change in your code and posted code snippet, please review it and let me know if you have some other expectation :) – Jigar7521 Oct 18 '16 at 09:34

3 Answers3

5

According to this answer, when you want to erase a line, you need to clear the entire canvas and then redraw all.

In my suggestion, you should have an array that contains the lines that are active and check it before drawing a line in order to draw or erase the line you're checking.

I suggest add an array lines, a function redrawAll and a function checkLine like I added into your snippet:

var lastSelection;
var lines = new Array();

// Add click listener for answer-container
function listenToClick() {
    var rows = document.querySelectorAll('.row'),
        row;
    var cols, col;

    for (row = 0; row < rows.length; row++) {
        cols = rows[row].children;

        for (col = 0; col < cols.length; col++) {
            // Bind information about which answer is this,
            // so we can prevent from connecting two answers on
            // same column.
            cols[col].addEventListener('click', selectAnswer.bind({
                row: row,
                col: col,
                element: cols[col]
            }));
        }
    }
}

function checkLine(p1, p2)
{
 var line;
 for (var i = 0; i < lines.length; i++)
    {
     line = lines[i];
     if (line.p1.x == p1.x && line.p1.y == p1.y
           && line.p2.x == p2.x && line.p2.y == p2.y)
        {
            //line exists, remove it from lines
            lines.splice(i, 1);
         return true;
        }
        else if (line.p1.x == p2.x && line.p1.y == p2.y
           && line.p2.x == p1.x && line.p2.y == p1.y)
        {
            //line exists, remove it from lines
            lines.splice(i, 1);
         return true;
        }
    }
    return false;
}

function redrawAll()
{
    for (var i = 0; i < lines.length; i++)
    {
        drawLine(lines[i].p1, lines[i].p2);
    }
}

// This is fired when a answer-container is clicked.
function selectAnswer(event) {
    if (lastSelection) {
       lastSelection.element.classList.remove('selected');
    }

    if (!lastSelection || lastSelection.col === this.col)
    {
        lastSelection = this;
        this.element.classList.add('selected');
    } 
    else 
    {
        if ( checkLine(getPoint(lastSelection.element), getPoint(this.element)) )
        {
            var canvas = document.getElementById("connection-canvas");
            var ctx = canvas.getContext("2d");
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
            redrawAll();
        }
        else
        {
            var line = {
             'p1': getPoint(this.element), 
             'p2': getPoint(lastSelection.element)
         };
         drawLine(line.p1, line.p2);
            lines.push(line);
        }
        console.log(lines);
        lastSelection = null;
    }
}

function getPoint(answerElement) {
    var roundPointer = answerElement.lastElementChild;
    return {
        y: answerElement.offsetTop + roundPointer.offsetTop + roundPointer.offsetHeight / 2,
        x: answerElement.offsetLeft + roundPointer.offsetLeft + roundPointer.offsetWidth / 2
    };
}

function drawLine(p1, p2) {
    var canvas = document.getElementById("connection-canvas");
    var ctx = canvas.getContext("2d");

    ctx.beginPath();
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
    ctx.stroke();
}

function resizeCanvas() {
    var canvas = document.getElementById("connection-canvas");
    var ctx = canvas.getContext("2d");

    ctx.canvas.width  = window.innerWidth;
    ctx.canvas.height = window.innerHeight;
}

listenToClick();
resizeCanvas();
.padding-answer-line-mapping
{
    padding-bottom:8px;
}

.answer-container
{
    width:40px;
    height:40px;
    background-color:#ccc;
    border:1px solid #ccc;
    margin:2px;
    float:left;
    text-align:center;
    padding-top:8px;
    cursor:pointer;
    position:relative;
}

.answer-container:hover, .answer-container:focus, .answer-container:active
{
    background-color: #0076e9;
    color: white;
    border: 1px solid #0076e9;
}

.round-pointer-right
{
    position: absolute;
    background-color:#ccc;
    cursor:pointer;
    width:10px;
    height:10px;
    border-radius: 50%;
    right:0px;
    top:14px;
    margin-right:-20px;
}

.round-pointer-left
{
    position: absolute;
    background-color:#ccc;
    cursor:pointer;
    width:10px;
    height:10px;
    border-radius: 50%;
    left:0px;
    top:14px;
    margin-left:-20px;
}

.selected {
    background-color: red;
}
<link href="//code.ionicframework.com/1.3.1/css/ionic.css" rel="stylesheet"/>
Match the following items.

 <canvas id="connection-canvas" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"></canvas>
<div class="row padding-answer-line-mapping">
    <div class="col answer-container">
        One
        <div class="round-pointer-right"></div>
    </div>
    <div class="col">
        
    </div>
    <div class="col answer-container">
        2
        <div class="round-pointer-left"></div>
    </div>
</div>
<div class="row padding-answer-line-mapping">
    <div class="col answer-container">
        Two
        <div class="round-pointer-right"></div>
    </div>
    <div class="col">
        
    </div>
    <div class="col answer-container">
        1
        <div class="round-pointer-left"></div>
    </div>
</div>
Community
  • 1
  • 1
dubafek
  • 1,073
  • 9
  • 21
  • Your answer was really good..keep this your sniplet... Just one more thing, can you show me another snippet that only single line allowed? Thanks. – Nere Oct 15 '16 at 20:41
  • In your snippet you can add the line `ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);` after the line that has `var ctx = ...` in the `drawLine` function. That way you erase the previous line before drawing the next one. – dubafek Oct 16 '16 at 03:56
  • I tried, this will make all lines cleared. How to clear only specific line? – Nere Oct 16 '16 at 22:40
  • Can you show me how to get the outputs from lines connected?E.g When user click `one` and `1` then the result return in JSON [one,1], etc. If user select multiple answers - user click `one` then (`1` and `2`) then the result would be `[{left:'one',right:[1,2]}]`. Any helps would be appreciated. – Nere Oct 17 '16 at 00:14
  • My suggestion is to add `id` in the html objects, then read them before adding the line and add them in the `var line` object that I've added. Then you'll have all lines and their info in the `lines` array. Am I being clear? – dubafek Oct 18 '16 at 17:50
  • Yes...definitely. – Nere Oct 19 '16 at 01:59
5

Let me know if you have any issue.

var lastSelection;
var p = 0;
var canvasPoints = [];

function listenToClick() {
  var rows = document.querySelectorAll('.row'),
    row;
  var cols, col;

  for (row = 0; row < rows.length; row++) {
    cols = rows[row].children;

    for (col = 0; col < cols.length; col++) {
      cols[col].addEventListener('click', selectAnswer.bind({
        row: row,
        col: col,
        element: cols[col]
      }));
    }
  }
}
var question = null;
var answer = null;

// This is fired when a answer-container is clicked.
function selectAnswer(event) {

  if (this.element.classList.contains("answer")) {
    answer = this.element;
  } else if (this.element.classList.contains("question")) {
    question = this.element;
    answer = null;
  }
  if (question && answer) {
    if (!removeObjects()) {
      var points = {};
      points.answer = getPoint(answer);
      points.question = getPoint(question);
      canvasPoints.push(points);
    }
  } else if (answer) {
    console.log("Please select Left option");
  }
  resizeCanvas();
}

function getPoint(answerElement) {
  var roundPointer = answerElement.lastElementChild;
  return {
    y: answerElement.offsetTop + roundPointer.offsetTop + roundPointer.offsetHeight / 2,
    x: answerElement.offsetLeft + roundPointer.offsetLeft + roundPointer.offsetWidth / 2,
    text: answerElement.innerText
  };
}

function drawLine(p1, p2) {
  var canvas = document.getElementById("connection-canvas");
  var ctx = canvas.getContext("2d");
  ctx.beginPath();
  ctx.moveTo(p1.x, p1.y);
  ctx.lineTo(p2.x, p2.y);
  ctx.stroke();
}

function resizeCanvas() {
  var canvas = document.getElementById("connection-canvas");
  var ctx = canvas.getContext("2d");
  ctx.canvas.width = window.innerWidth;
  ctx.canvas.height = window.innerHeight;

  for (var i = 0; i < canvasPoints.length; i++) {
    drawLine(canvasPoints[i].answer, canvasPoints[i].question);
  }
  output();
}

function removeObjects() {

  var answerPoints = getPoint(answer);
  var questionPoints = getPoint(question);
  for (var i = 0; i < canvasPoints.length; i++) {

    if (canvasPoints[i].answer.x == answerPoints.x && canvasPoints[i].answer.y == answerPoints.y && canvasPoints[i].question.x == questionPoints.x && canvasPoints[i].question.y == questionPoints.y) {
      canvasPoints.splice(i, 1);
      return true;
    }
  }
  return false;
}
listenToClick();
resizeCanvas();

function output() {
  var outputObject = [];
  for (var i = 0; i < canvasPoints.length; i++) {
    var obj = {
      "left": canvasPoints[i].question.text,
      right: []
    };
    for (var j = 0; j < outputObject.length; j++) {
      if (outputObject[j].left == canvasPoints[i].question.text) {
        obj = outputObject[j];
        outputObject.splice(j, 1);
      }
    }
    obj.right.push(canvasPoints[i].answer.text)
    outputObject.push(obj);
  }
  console.log(outputObject);
}
.padding-answer-line-mapping {
  padding-bottom: 8px;
}
.answer-container {
  width: 40px;
  height: 40px;
  background-color: #ccc;
  border: 1px solid #ccc;
  margin: 2px;
  float: left;
  text-align: center;
  padding-top: 8px;
  cursor: pointer;
  position: relative;
}
.answer-container:hover,
.answer-container:focus,
.answer-container:active {
  background-color: #0076e9;
  color: white;
  border: 1px solid #0076e9;
}
.round-pointer-right {
  position: absolute;
  background-color: #ccc;
  cursor: pointer;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  right: 0px;
  top: 14px;
  margin-right: -20px;
}
.round-pointer-left {
  position: absolute;
  background-color: #ccc;
  cursor: pointer;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  left: 0px;
  top: 14px;
  margin-left: -20px;
}
.selected {
  background-color: red;
}
<link href="//code.ionicframework.com/1.3.1/css/ionic.css" rel="stylesheet" />Match the following items.

<canvas id="connection-canvas" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"></canvas>
<div class="row padding-answer-line-mapping" id="id1">
  <div class="col answer-container question" id="id1-One">
    One
    <div class="round-pointer-right"></div>
  </div>
  <div class="col" id="id1-cols">

  </div>
  <div class="col answer-container answer" id="id1-2">
    2
    <div class="round-pointer-left"></div>
  </div>
</div>
<div class="row padding-answer-line-mapping" id="id2">
  <div class="col answer-container question" id="id2-two">
    Two
    <div class="round-pointer-right"></div>
  </div>
  <div class="col" id="id2-cols">

  </div>
  <div class="col answer-container answer" id="id2-1">
    1
    <div class="round-pointer-left"></div>
  </div>
</div>
Mitul
  • 3,431
  • 2
  • 22
  • 35
  • Very nice.... Just one more steps to fulfill my requirement. Can you show me how to get the outputs from lines connected?E.g When user click one and 1 then the result return in JSON [one,1], etc. If user select multiple answers - user click one then (1 and 2) then the result would be [{left:'one',right:[1,2]}]. Any helps would be appreciated. – Nere Oct 19 '16 at 09:30
  • 2
    Yes sure give me couple of minute i will update the ans So which type of relation ship you want one to one, one to many or many to many – Mitul Oct 19 '16 at 09:34
  • Very nice...I appreciate your effort. – Nere Oct 19 '16 at 09:35
  • 1
    @Imran We need to put the restrictions for to select first left and then he can select right. – Mitul Oct 19 '16 at 09:57
  • And what about the ans is like one and 1 = `[{left:'one',right:[1]}]` , One and 1 , 2 `[{left:'one',right:[1,2]}]` Will it work – Mitul Oct 19 '16 at 10:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/126096/discussion-between-mitul-and-imran). – Mitul Oct 19 '16 at 10:34
  • Very good! Thank you very much! I will accept your answer after 2 days. Is it ok? – Nere Oct 19 '16 at 10:45
  • I will share your answer with my friends. – Nere Oct 19 '16 at 10:46
  • 1
    Ok no problem take your time, – Mitul Oct 19 '16 at 10:55
  • @Mitul nice answer :) – Brijal Savaliya Oct 20 '16 at 09:00
1

I have used your existing code and made some change into it, please review this snippet and let me know, this is what you want?

var lastSelection;
    var p = 0;
// Add click listener for answer-container
    function listenToClick() {
        var rows = document.querySelectorAll('.row'),
                row;
        var cols, col;

        for (row = 0; row < rows.length; row++) {
            cols = rows[row].children;

            for (col = 0; col < cols.length; col++) {
                // Bind information about which answer is this,
                // so we can prevent from connecting two answers on
                // same column.
                cols[col].addEventListener('click', selectAnswer.bind({
                    row: row,
                    col: col,
                    element: cols[col]
                }));
            }
        }
    }

// This is fired when a answer-container is clicked.
    function selectAnswer(event) {
        
        if (lastSelection) {
            lastSelection.element.classList.remove('selected');
        }        
        
        if (!lastSelection || lastSelection.col === this.col) {
            lastSelection = this;
            this.element.classList.add('selected');
            
            if(p%2==0){
                resizeCanvas();
            }
            p++;
        } else {
           
            drawLine(getPoint(this.element), getPoint(lastSelection.element));
            lastSelection = null;
        }
        
    }

    function getPoint(answerElement) {
      //  console.log('getpoint : ' + JSON.stringify(answerElement));

        var roundPointer = answerElement.lastElementChild;
        //console.log('roundPointer : ' + JSON.stringify(roundPointer));

        return {
            y: answerElement.offsetTop + roundPointer.offsetTop + roundPointer.offsetHeight / 2,
            x: answerElement.offsetLeft + roundPointer.offsetLeft + roundPointer.offsetWidth / 2
        };
    }

    function drawLine(p1, p2) {


        var canvas = document.getElementById("connection-canvas");
        var ctx = canvas.getContext("2d");

        ctx.beginPath();
        ctx.moveTo(p1.x, p1.y);
        ctx.lineTo(p2.x, p2.y);
        ctx.stroke();
    }

    function resizeCanvas() {
        var canvas = document.getElementById("connection-canvas");
        var ctx = canvas.getContext("2d");

        ctx.canvas.width = window.innerWidth;
        ctx.canvas.height = window.innerHeight;
    }

    listenToClick();
    resizeCanvas();
.padding-answer-line-mapping
{
    padding-bottom:8px;
}

.answer-container
{
    width:40px;
    height:40px;
    background-color:#ccc;
    border:1px solid #ccc;
    margin:2px;
    float:left;
    text-align:center;
    padding-top:8px;
    cursor:pointer;
    position:relative;
}

.answer-container:hover, .answer-container:focus, .answer-container:active
{
    background-color: #0076e9;
    color: white;
    border: 1px solid #0076e9;
}

.round-pointer-right
{
    position: absolute;
    background-color:#ccc;
    cursor:pointer;
    width:10px;
    height:10px;
    border-radius: 50%;
    right:0px;
    top:14px;
    margin-right:-20px;
}

.round-pointer-left
{
    position: absolute;
    background-color:#ccc;
    cursor:pointer;
    width:10px;
    height:10px;
    border-radius: 50%;
    left:0px;
    top:14px;
    margin-left:-20px;
}

.selected {
    background-color: red;
}
<link href="//code.ionicframework.com/1.3.1/css/ionic.css" rel="stylesheet"/>
Match the following items.

<canvas id="connection-canvas" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"></canvas>
<div class="row padding-answer-line-mapping" id="id1">
    <div class="col answer-container" id="id1-One">
        One
        <div class="round-pointer-right"></div>
    </div>
    <div class="col" id="id1-cols">

    </div>
    <div class="col answer-container" id="id1-2">
        2
        <div class="round-pointer-left"></div>
    </div>
</div>
<div class="row padding-answer-line-mapping" id="id2">
    <div class="col answer-container" id="id2-two">
        Two
        <div class="round-pointer-right"></div>
    </div>
    <div class="col"  id="id2-cols">

    </div>
    <div class="col answer-container" id="id2-1">
        1
        <div class="round-pointer-left"></div>
    </div>
</div>
Jigar7521
  • 1,549
  • 14
  • 27
  • It still not working properly, when I click double times then it will reset all lines. It should reset the the current box only. – Nere Oct 18 '16 at 09:42
  • 1
    Yes yes, i am working on it , will come back soon with result. – Jigar7521 Oct 18 '16 at 09:51
  • @Imran : I have tried a lot, the problem only is you had used common canvas element to draw both line. Isn't it right? – Jigar7521 Oct 18 '16 at 10:47
  • I was satisfied with dubafek answer. Just a few steps more to fulfill my requirement. You can start with last code by dubafek... Can you show me how to get the outputs from lines connected?E.g When user click one and 1 then the result return in JSON [one,1], etc. If user select multiple answers - user click one then (1 and 2) then the result would be [{left:'one',right:[1,2]}]. Any helps would be appreciated. – Nere Oct 18 '16 at 11:02