Michael David
Michael David

Reputation: 113

Is there a way to merge divs together to form one div?

I am working on a project where users can work around with a list of information. I'll explain with the code below.

<div>
  <div id="factmarker1" onmouseup="factFunction(1)">
     <b class="fact-num" id="fact-num1">1</b><mark id="fact1">The earth is spherical</mark>
  </div>
  <div id="factmarker2" onmouseup="factFunction(2)">
     <b class="fact-num" id="fact-num2">2</b><mark id="fact2">There is no gravity in space</mark>
  </div>
  <div id="factmarker3" onmouseup="factFunction(3)">
     <b class="fact-num" id="fact-num3">3</b><mark id="fact3">The earth is made up of more water</mark>
  </div>
  <div id="factmarker4" onmouseup="factFunction(4)">
     <b class="fact-num" id="fact-num4">4</b><mark id="fact4">There is a lunar year every four years</mark>
  </div>
  <div id="factmarker5" onmouseup="factFunction(5)">
     <b class="fact-num" id="fact-num5">5</b><mark id="fact5">Every lunar year has 366 days</mark>
  </div>
  <div id="factmarker6" onmouseup="factFunction(6)">
     <b class="fact-num" id="fact-num6">6</b><mark id="fact6">The earth is spherical</mark>
  </div>
  <div id="factmarker7" onmouseup="factFunction(7)">
     <b class="fact-num" id="fact-num7">7</b><mark id="fact7">The earth is spherical</mark>
  </div>
</div>
<div id="previewFact"></div>

Now this is pretty straightforward. If a user clicks or highlights any of the facts, an options tab pops up enabled by javascript where the user has options to do a lot of things like copying, saving or sharing each fact. If a user highlights say fact 5 and chooses to save it, the preview prompt shows something like this:

/*
FACT 5

Every lunar year has 366 days

Now here's my problem: I want to be able to allow a user highlight multiple facts at the same time and I can't find a way to do it. If a user finds fact 1 through 4 interesting and highlights it, I want the preview to be something like this:

/*
FACT 1 to 4

1 The earth is spherical
2 There is no gravity in space
3 The earth is made up of more water
4 There is a lunar year every four years
*/

The user should also be able to choose any multiple facts. Also, the facts are called out from a database using PHP, so, I don't know how many facts there are or what the contents are.

I have tried using the window.getSelection() method. I tried using "fact-num" class so that when I got the content of the highlighted area, I could search for the classes inside it - like so:

function factFunction(factNum) {
  var sel = window.getSelection();
  var factNumLength = sel.querySelectorAll('.fact-num').length;
  if(factNumLength > 1){ //checking if it is a multiple highlight or not
    var factStart = sel.querySelectorAll('.fact-num')[0].innerHTML;
    var factStop = sel.querySelectorAll('.fact-num')[factNumLength - 1].innerHTML;
    var preview = "Fact "+factStart+" to "+factStop;
    preview += "<br/>"+sel.toString();
    document.getElementById('previewFact').innerHTML = preview;
  }else{
    var preview = "Fact "+factNum;
    preview += "<br/>"+sel.toString();
    document.getElementById('previewFact').innerHTML = preview;
  }
}

This isn't the entire block of code as it is much longer, but this is the general structure of the entire thing and it's not working. I don't know if I am supposed to use the value of the window.getSelection() as I am using it.

I would really appreciate any help as I have spent the whole of yesterday and most of today looking for answers.

Thank you

Edit

Just so I'm properly understood, when highlighted like this: enter image description here It should show this: enter image description here

Upvotes: 0

Views: 613

Answers (2)

t.niese
t.niese

Reputation: 40882

I wasn't able to find anywhere in the specification that Selection provides querySelectorAll but even if it would exist, containsNode with true for partial might be the better choice.

Furthermore, you want to utilize event delegation instead of attaching the event handler to every element. That way you are more flexible about the logic you want to apply.

With data- attributes you can give the elements some additional semantic information like your facts id.

I cloned the facts elements in displayFactsResult but you can for sure change that again to match your desired output.

The final code could look something like this (with further comments what each part of the code does):

function displayFactsResult(array) {
  const previewFact = document.getElementById("previewFact");

  // empty the facts preview
  previewFact.innerHTML = '';

  // create the info how many slected
  const infoNode = document.createElement('div')
  if (array.length == 1) {
    infoNode.textContent = 'Fact ' + array[0].dataset.fact
  } else if (array.length > 1) {
    infoNode.textContent = 'Facts ' + array[0].dataset.fact + ' to ' + array[array.length-1].dataset.fact
  }
  previewFact.appendChild(infoNode)

  // clone each of the selected facts and append them to the preview
  array.forEach(node => {
    previewFact.appendChild(node.cloneNode(true));
  })

}

// use event delegation instead of attaching the event handler to each element
document.querySelector('.facts').addEventListener('mouseup', (e) => {

  // traverse the DOM up until on element with `data-fact` or `.facts` element is reached
  let element = e.target
  while (element != e.currentTarget && !element.dataset.fact) {
    element = element.parentNode;
  }

  // get the selection
  var sel = window.getSelection();

  // get all elements, and filter by those in the selection
  var factsInSelection = Array.from(document.querySelectorAll('[data-fact]'))
    .filter(node => {
      return sel.containsNode(node, true)
    });

  if (factsInSelection.length > 0) {
    // if there is at least one fact selected pass it to the display function
    displayFactsResult(factsInSelection)
  } else if (element.dataset.fact) {
    // if the element at which we stopped traversing has `data-fact` use this to display
    displayFactsResult([element])
  } else {
    displayFactsResult([])
  }
})
<div class="facts">
  <div id="factmarker1" data-fact="1">
    <b class="fact-num" id="fact-num1">1</b><mark id="fact1">The earth is spherical</mark>
  </div>
  <div id="factmarker2" data-fact="2">
    <b class="fact-num" id="fact-num2">2</b><mark id="fact2">There is no gravity in space</mark>
  </div>
  <div id="factmarker3" data-fact="3">
    <b class="fact-num" id="fact-num3">3</b><mark id="fact3">The earth is made up of more water</mark>
  </div>
  <div id="factmarker4" data-fact="4">
    <b class="fact-num" id="fact-num4">4</b><mark id="fact4">There is a lunar year every four years</mark>
  </div>
  <div id="factmarker5" data-fact="5">
    <b class="fact-num" id="fact-num5">5</b><mark id="fact5">Every lunar year has 366 days</mark>
  </div>
  <div id="factmarker6" data-fact="6">
    <b class="fact-num" id="fact-num6">6</b><mark id="fact6">The earth is spherical</mark>
  </div>
  <div id="factmarker7" data-fact="7">
    <b class="fact-num" id="fact-num7">7</b><mark id="fact7">The earth is spherical</mark>
  </div>
</div>
<hr>
<div id="previewFact">
</div>

Upvotes: 3

dave
dave

Reputation: 2901

I've updated this to make an array of objects containing the fact ID and text that checks against the fact ID to avoid duplicates, and saves all clicked facts into the previewFact div.

let savedFacts = [];

$('.fact').click(function() {

  let factText = $(this).children('mark').text();
  let factId = $(this).children('.fact-num').text();
  
  if (savedFacts.findIndex(x => x.id === factId) == -1) {

    savedFacts.push({"id":factId,"fact":factText})

  }
  
  $("#previewFact").html(savedFacts.map(function(elem){
        return `${elem.id} ${elem.fact}`;
    }).join("<br />"));
  
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <div id="factmarker1" class="fact">
     <b class="fact-num" id="fact-num1">1</b><mark id="fact1">The earth is spherical</mark>
  </div>
  <div id="factmarker2" class="fact">
     <b class="fact-num" id="fact-num2">2</b><mark id="fact2">There is no gravity in space</mark>
  </div>
  <div id="factmarker3"  class="fact" >
     <b class="fact-num" id="fact-num3">3</b><mark id="fact3">The earth is made up of more water</mark>
  </div>
  <div id="factmarker4"  class="fact" >
     <b class="fact-num" id="fact-num4">4</b><mark id="fact4">There is a lunar year every four years</mark>
  </div>
  <div id="factmarker5"  class="fact">
     <b class="fact-num" id="fact-num5">5</b><mark id="fact5">Every lunar year has 366 days</mark>
  </div>
  <div id="factmarker6"  class="fact" >
     <b class="fact-num" id="fact-num6">6</b><mark id="fact6">The earth is spherical</mark>
  </div>
  <div id="factmarker7"  class="fact">
     <b class="fact-num" id="fact-num7">7</b><mark id="fact7">The earth is spherical</mark>
  </div>
</div>
<br />
<span>Saved facts:</span>
<div id="previewFact"></div>

Upvotes: 0

Related Questions