Skip to content
Advertisement

Vanilla JavaScript: How to dynamically create a button that shows up after user has selected some text and then do something with that text?

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';
        });
    });
});
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement