Reputation: 113
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:
It should show this:
Upvotes: 0
Views: 613
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
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