简体   繁体   中英

Single-click only works once

I want to allow the user to remove words in a div by a single mouse click. It work fine, see jsFiddle .

The only problem is that the single-click feature only works on the first click. After that, you need to double click.

I can't get my head around why it is behaving like this. Maybe you can? Could be problem with jQuery(document).ready() ...

jQuery:

// highlight a word/term quicker and smarter: so.com/a/35103840/1185126
jQuery(document).ready(function(e){

    (function(els){

        // variable declaration for previous range info
        // and function for finding the sibling
        var prevRangeInfo = {},
            findSibling = function(thisNode, direction){
                // get the child node list of the parent node
                var childNodeList = thisNode.parentNode.childNodes,
                    children = [];

                // convert the child node list to an array
                for(var i=0, l=childNodeList.length; i<l; i++) children.push(childNodeList[i]);

                return children[children.indexOf(thisNode) + direction];
            };

        for(var i=0;i<els.length;i++){
            var el = els[i];

            el.addEventListener('mouseup',function(evt){
                if (document.createRange) { // Works on all browsers, including IE 9+

                    var selected = window.getSelection();
                    // Removing the following line from comments will make the function drag-only
                    /* if(selected.toString().length){ */
                    var d = document,
                        nA = selected.anchorNode,
                        oA = selected.anchorOffset,
                        nF = selected.focusNode,
                        oF = selected.focusOffset,
                        range = d.createRange(),
                        rangeLength = 0;

                    range.setStart(nA,oA);
                    range.setEnd(nF,oF);

                    // Check if direction of selection is right to left
                    if(range.startContainer !== nA || (nA === nF && oF < oA)){
                        range.setStart(nF,oF);
                        range.setEnd(nA,oA);
                    }

                    // Extend range to the next space or end of node
                    while(range.endOffset < range.endContainer.textContent.length && !/\s$/.test(range.toString())){
                        range.setEnd(range.endContainer, range.endOffset + 1);
                    }
                    // Extend range to the previous space or start of node
                    while(range.startOffset > 0 && !/^\s/.test(range.toString())){
                        range.setStart(range.startContainer, range.startOffset - 1);
                    }

                    // Remove spaces
                    if(/\s$/.test(range.toString()) && range.endOffset > 0)
                        range.setEnd(range.endContainer, range.endOffset - 1);
                    if(/^\s/.test(range.toString()))
                        range.setStart(range.startContainer, range.startOffset + 1);

                    // Store the length of the range
                    rangeLength = range.toString().length;

                    // Check if another range was previously selected
                    if(prevRangeInfo.startContainer && nA === nF && oA === oF){
                        var rangeTryContain = d.createRange(),
                            rangeTryLeft = d.createRange(),
                            rangeTryRight = d.createRange(),
                            nAp = prevRangeInfo.startContainer;
                        oAp = prevRangeInfo.startOffset;
                        nFp = prevRangeInfo.endContainer;
                        oFp = prevRangeInfo.endOffset;

                        rangeTryContain.setStart(nAp, oAp);
                        rangeTryContain.setEnd(nFp, oFp);
                        rangeTryLeft.setStart(nFp, oFp-1);
                        rangeTryLeft.setEnd(range.endContainer, range.endOffset);
                        rangeTryRight.setStart(range.startContainer, range.startOffset);
                        rangeTryRight.setEnd(nAp, oAp+1);

                        // Store range boundary comparisons
                        // & inner nodes close to the range boundary --> stores null if none
                        var compareStartPoints = range.compareBoundaryPoints(0, rangeTryContain) === 0,
                            compareEndPoints = range.compareBoundaryPoints(2, rangeTryContain) === 0,
                            leftInnerNode = range.endContainer.previousSibling,
                            rightInnerNode = range.startContainer.nextSibling;

                        // Do nothing if clicked on the right end of a word
                        if(range.toString().length < 1){
                            range.setStart(nAp,oAp);
                            range.setEnd(nFp,oFp);
                        }

                        // Collapse the range if clicked on last highlighted word
                        else if(compareStartPoints && compareEndPoints)
                            range.collapse();

                        // Remove a highlighted word from left side if clicked on
                        // This part is quite tricky!
                        else if(compareStartPoints){
                            range.setEnd(nFp,oFp);

                            if(range.startOffset + rangeLength + 1 >= range.startContainer.length){
                                if(rightInnerNode)
                                // there is a right inner node, set its start point as range start
                                    range.setStart(rightInnerNode.firstChild, 0);

                                else {
                                    // there is no right inner node
                                    // there must be a text node on the right side of the clicked word

                                    // set start of the next text node as start point of the range
                                    var rightTextNode = findSibling(range.startContainer.parentNode, 1),
                                        rightTextContent = rightTextNode.textContent,
                                        level=1;

                                    // if beginning of paragraph, find the first child of the paragraph
                                    if(/^(?:\r\n|[\r\n])|\s{2,}$/.test(rightTextContent)){
                                        rightTextNode = findSibling(rightTextNode, 1).firstChild;
                                        level--;
                                    }

                                    range.setStart(rightTextNode, level);

                                }
                            }
                            else
                                range.setStart(range.startContainer, range.startOffset + rangeLength + 1);
                        }

                        // Remove a hightlighted word from right side if clicked on
                        // This part is also tricky!
                        else if (compareEndPoints){
                            range.setStart(nAp,oAp);

                            if(range.endOffset - rangeLength - 1 <= 0){
                                if(leftInnerNode)
                                // there is a right inner node, set its start point as range start
                                    range.setEnd(leftInnerNode.lastChild, leftInnerNode.lastChild.textContent.length);

                                else {
                                    // there is no left inner node
                                    // there must be a text node on the left side of the clicked word

                                    // set start of the previous text node as start point of the range
                                    var leftTextNode = findSibling(range.endContainer.parentNode, -1),
                                        leftTextContent = leftTextNode.textContent,
                                        level = 1;

                                    // if end of paragraph, find the last child of the paragraph
                                    if(/^(?:\r\n|[\r\n])|\s{2,}$/.test(leftTextContent)){
                                        leftTextNode = findSibling(leftTextNode, -1).lastChild;
                                        level--;
                                    }

                                    range.setEnd(leftTextNode, leftTextNode.length - level);
                                }
                            }
                            else
                                range.setEnd(range.endContainer, range.endOffset - rangeLength - 1);
                        }

                        // Add previously selected range if adjacent
                        // Upgraded to include previous/next word even in a different paragraph
                        else if(/^[^\s]*((?:\r\n|[\r\n])|\s{1,})[^\s]*$/.test(rangeTryLeft.toString()))
                            range.setStart(nAp,oAp);
                        else if(/^[^\s]*((?:\r\n|[\r\n])|\s{1,})[^\s]*$/.test(rangeTryRight.toString()))
                            range.setEnd(nFp,oFp);

                        // Detach the range objects we are done with, clear memory
                        rangeTryContain.detach();
                        rangeTryRight.detach();
                        rangeTryLeft.detach();
                    }

                    // Save the current range --> not the whole Range object but what is neccessary
                    prevRangeInfo = {
                        startContainer: range.startContainer,
                        startOffset: range.startOffset,
                        endContainer: range.endContainer,
                        endOffset: range.endOffset
                    };

                    // Clear the saved range info if clicked on last highlighted word
                    if(compareStartPoints && compareEndPoints)
                        prevRangeInfo = {};

                    // Remove all ranges from selection --> necessary due to potential removals
                    selected.removeAllRanges();

                    // Assign the current range as selection
                    selected.addRange(range);

                    // Detach the range object we are done with, clear memory
                    range.detach();

                    el.style.MozUserSelect = '-moz-none';

                    // Removing the following line from comments will make the function drag-only
                    /* } */

                } else {
                    // Fallback for Internet Explorer 8 and earlier
                    // (if you think it still is worth the effort of course)
                }
            });

            /* This part is necessary to eliminate a FF specific dragging behavior */
            el.addEventListener('mousedown',function(e){
                if (window.getSelection) {  // Works on all browsers, including IE 9+
                    var selection = window.getSelection ();
                    selection.collapse (selection.anchorNode, selection.anchorOffset);
                } else {
                    // Fallback for Internet Explorer 8 and earlier
                    // (if you think it still is worth the effort of course)
                }
                el.style.MozUserSelect = 'text';
            });

        }
    })(document.getElementsByClassName('taggable'));

});




// remove selected text
jQuery(document).ready(function() {
    jQuery('.taggable').bind("mouseup", function() {
    var text1;
        if (window.getSelection().toString() != "") {
            selectedText = window.getSelection().toString()
            text1 = jQuery(".taggable").text().split("")
            pointStart = window.getSelection().anchorOffset
            pointEnd = window.getSelection().focusOffset

            if (pointEnd < pointStart) {
                pointStart = pointEnd
            }
            text1.splice(pointStart, selectedText.length);
            text1 = text1.join("")
        } else {
            selectedText = jQuery(".taggable").text()
            text1 = selectedText;
        }
    jQuery(".taggable").text(text1);

    });
});

This might not solve your issue, but it's another approach.
This code wrap every word inside a span and create an event listener for each.


HTML

<p>This is an example text</p>


Javascript

jQuery(document).ready(function($) {
    var text = $('p').text();
    var arr = text.split(' ');

    $('p').html('');

    for (var i = 0; i < arr.length; i++) {
        $('<span />').html(arr[i] + ' ').appendTo('p');

        $('p').on('click', 'span:nth-of-type(' + (i + 1) + ')', function() {
            $(this).remove();
        });
    }
});

I've figured out that the main problem is because if the anchorOffset is equal to focusOffset , it doesn't work, so, the possible solution is to add +1 when it's equal and then the code will work as desired since it finds at least a letter when substring. Change the code:

from

var selected = window.getSelection();
                    // Removing the following line from comments will make the function drag-only
                    /* if(selected.toString().length){ */
                    var d = document,
                        nA = selected.anchorNode,
                        oA = selected.anchorOffset,
                        nF = selected.focusNode,
                        oF = selected.focusOffset,
                        range = d.createRange(),
                        rangeLength = 0;

to

var selected = window.getSelection();
                    var offset = selected.focusOffset;
                    if(selected.anchorOffset == selected.focusOffset)
                          offset++;
                    // Removing the following line from comments will make the function drag-only
                    /* if(selected.toString().length){ */
                    var d = document,
                        nA = selected.anchorNode,
                        oA = selected.anchorOffset,
                        nF = selected.focusNode,
                        oF = offset,
                        range = d.createRange(),
                        rangeLength = 0;

I've tested many times here on JFiddle and it worked OK, but I'm still afraid that this can result in some other problems.

If you get some problem, please notify me and i'll help.

EDIT:

jsFiddle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM