必须回答我自己的问题,以供将来参考。 感谢 Tyler Durden 和 Endoxos 的解决方案,在玩了几个小时之后,现在(大部分情况下)这是我想要它做的代码(它也被注释掉了答案以便于理解):
/* 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';
});
});
});