I’m working on a little project in which users can upload their .docx files and then read those files (and files from other users) right in the app. For this purpose I have extracted text from docx and displayed it in a div in separate view and html page. Now, I would like to give my users an option to select some text from this div (and only this div) and when they have selected it, I would like to display a button that hovers over that text on which they can click. This button would be for adding selected text to their notes, similar to what MS Edge does automatically when we select some text (it adds those three dots which then open a menu for copying and the rest). Or even more precisely, if you’re familiar with Coursera website – when some text from lessons is selected, a ‘Save Note’ button appears and clicking it adds the selected text right in your notes without the need for users to do anything else, and they can find the saved note whenever they go to their notes (photo bellow shows this button in action).
Coursera ‘Save Note’ button example
However, I’m not sure how to implement this. I think I would use window.getSelection, and then store the selection in some const that I would then send via fetch to my server to add it to the Notes Model (I’m using Django). But I don’t know how to implement this, nor where to even start. How to even make a button hover over other text like this, only when a selection is selected? Any kind of help is much appreciated!! Note: I would like to this in Vanilla JS if possible, since I’m not yet familiar with React or other libraries/frameworks.
Advertisement
Answer
Had to answer my own question, for future references. Thanks to solutions from Tyler Durden and Endoxos, after a few hours playing with it, this is now the code that (for the most part) does what I wanted it to do (it’s also commented out for this answer for easier understanding):
/* Read view - dynamically adding Save Note button after selection of text */ document.addEventListener('DOMContentLoaded', function() { /* Use this functions only in a div that contains displayed contents of files */ const content = document.querySelector('#docContent'); /* Create and append button */ const noteBtn = document.createElement('button'); noteBtn.innerHTML = 'Save Note'; noteBtn.style.position = 'absolute'; noteBtn.style.display = 'none'; noteBtn.className = 'btn btn-sm btn-danger'; content.appendChild(noteBtn); let startX = 0; let startY = 0; /* On mousedown only save starting X and Y, but relevant to entire page, not the client X and Y, which causes button to stay on top part of doc, even if we want to select text from bottom part. */ content.addEventListener('mousedown', function(evt){ startX = evt.pageX; startY = evt.pageY; }); /* On mouse up, we check if the end X and Y differ from starting and if, we place the button to the end of the selection, where user's mouse will naturally be, after making the selection. This works on every part of the page and dom, except on the far right side (if selection ends on the endpoint on right side, that is), and for these cases one might make calculations and for those cases just reverse the direction of button, but I can't be bothered to do so today, maybe tomorrow... Also, if the start and end X and Y do not differ, most likely user wanted to click somewhere to 'hide' the popped up button, so we just set its display to none in such case*/ content.addEventListener('mouseup', function(evt) { if (evt.pageX != startX && evt.pageY != startY ) { noteBtn.style.top = `${evt.pageY}px`; noteBtn.style.left = `${evt.pageX}px`; noteBtn.style.display = 'block'; } else { noteBtn.style.display = 'none'; } }); /* Finally, we add event listener for clicks on button, and when the button is clicked we save the text to const, and pass that to our view in Django (in this case there is csrf_exempt, but normally one would do that too...) */ noteBtn.addEventListener('click', function() { const note = document.getSelection().toString(); const id = content.querySelector('.reading_content_id').value; fetch(`/add_note/${id}`, { method: 'POST', body: JSON.stringify({ note:`${note}` }) }).then (function() { document.getSelection().collapseToEnd(); noteBtn.style.display = 'none'; }); }); });