2017-03-04 6 views
2

リアルタイムHTMLハイライターを構築しているため、背景属性を持つスパン要素で囲まれたテキスト範囲をユーザーが選択すると、ここでマウスアップ時に選択したテキストを置換する

はフィドルです:

<span style="background-color: rgb(255, 255, 131);"> 
    r 
    <span style="background-color: rgb(255, 255, 131);"> 
     <span style="background-color: rgb(255, 255, 131);"> 
      e 
     </span> 
     p 
    </span> 
    r 
    <span style="background-color: rgb(255, 255, 131);"> 
     e 
    </span> 
    h 
    <span style="background-color: rgb(255, 255, 131);"> 
     end 
    </span> 
    e 
    <span style="background-color: rgb(255, 255, 131);"> 
     rit 
    </span> 
</span> 

聖ジャックポットバットマン:https://jsfiddle.net/4hd2vrex/

問題は、これは、ユーザーが複数の選択を行う際に、スパンがネストされた取得し、私はこのようなコンテンツを取得し、非常に厄介得ることができるということです!

スパンを追加する前に、選択したすべてのテキスト、スパンタグ、およびすべてを元の選択したテキストwindow.getSelection()に置き換えてください。私は上記のスパンの混乱を選択した場合

だから例えば、より多くのスパンで自分の選択したテキストをラップする前に、私はただのテキストreprehenderitあるwindow.getSelection()と、それらのスパンに代わると私はなるだろう。

<span style="background-color: rgb(255, 255, 131);">reprehenderit</span> 

Q: どのように私は選択たテキストで私の選択を交換するのですか?

+0

私はあなたが相互作用がどのように行われるかに考え直しを取ることをお勧めします。ユーザーは既に選択されている選択肢全体を選択するという意味ですか?選択の一部ですか?対話を減らし、シナリオを単純化する可能性があるからです。ちょうどそれが助ける場合にはちょうどコメントする:) – Alvaro

+0

興味深い...これ以上を簡素化する方法はまだ、機能を維持しているかわからない... –

+0

閉じる....しかしシガーなし:https://jsfiddle.net/4hd2vrex/2/ –

答えて

2

私は自分のやり方でハイライトテキスト全体をしましたが、window.Selection APIは使用しませんでしたが、使用する:select(start,end).then(merge).then(filter).then(highlight)。そして最も興味深いのは、テキストだけでも複雑な要素を強調することができるということです。 select apiはwysiwyg htmlエディタを書くこともできるので、選択問題で面白いみんなと​​共有して、あなたに助けてもらいたい、良い質問です!

(function (context, factory) { 
 
    if (typeof module != 'undefined' && typeof module.exports == 'object') { 
 
     module.exports = factory(context); 
 
    } else { 
 
     factory(context, true); 
 
    } 
 
})(window || this, function (context, bind) { 
 
    function promise(executor) { 
 
     return new Promise(executor); 
 
    } 
 

 
    var $TYPE = 'nodeType', $TEXT = 'textContent', $PARENT = 'parentNode', $NEXT = 'nextSibling', $FIRST = 'firstChild', NIL = {}; 
 

 
    function leaf(node) { 
 
     return node[$TYPE] == 3; 
 
    } 
 

 
    function next(node, tree) { 
 
     var it = tree ? node[$FIRST] || node[$NEXT] : node[$NEXT]; 
 
     if (it) { 
 
      if (leaf(it)) return it; 
 
      return next(it, true); 
 
     } 
 
     var parent = node[$PARENT]; 
 
     return parent && next(parent); 
 
    } 
 

 
    function parent(node) { 
 
     return node[$PARENT]; 
 
    } 
 

 
    function wrap(node, start, end) { 
 
     if (!node) throw 'node is null'; 
 
     if (!leaf(node)) throw 'node is not a leaf:' + node.tagName; 
 
     var rawText = node[$TEXT]; 
 
     var rawLength = rawText.length; 
 
     var self = { 
 
      node: node, 
 
      text: function (text) { 
 
       if (text !== undefined) { 
 
        node.textContent = text; 
 
        return wrap(node, 0, text.length); 
 
       } 
 
       return rawText.substring(self.start(), self.end()); 
 
      }, 
 
      is: function (other) { 
 
       return node == other.node; 
 
      }, 
 
      start: function() { 
 
       return start === NIL || !start ? 0 : start; 
 
      }, 
 
      end: function() { 
 
       return end === NIL || !end ? rawLength : end; 
 
      }, 
 
      length: function() { 
 
       return self.end() - self.start(); 
 
      }, 
 
      to: function (end) { 
 
       return wrap(node, self.start(), end.end()); 
 
      }, 
 
      toLast: function() { 
 
       return wrap(node, start, rawLength); 
 
      }, 
 
      next: function() { 
 
       var it = next(node); 
 
       return it && wrap(it); 
 
      }, 
 
      split: function() { 
 
       if (self.length() >= rawLength) return self; 
 
       var stack = [0].concat(self.start() || []).concat(self.end()).concat(self.end() != rawLength ? rawLength : []); 
 
       var start = stack.shift(); 
 
       var separated = []; 
 
       while (stack.length) { 
 
        var end = stack.shift(); 
 
        var text = document.createTextNode(rawText.substring(start, end)); 
 
        self.after(text); 
 
        separated.push(wrap(text)); 
 
        start = end; 
 
       } 
 
       self.remove(); 
 
       return !self.start() ? separated[0] : separated[1]; 
 
      }, 
 
      remove: function (optimized) { 
 
       var parent = node[$PARENT]; 
 
       if (optimized && parent.childNodes.length == 1) { 
 
        parent[$PARENT].removeChild(parent); 
 
       } 
 
       parent.removeChild(node); 
 
       return this; 
 
      }, 
 
      merge: function (other) { 
 
       var it = self.split(); 
 
       return it.text(other.split().remove(true).text() + it.text()); 
 
      }, 
 
      after: function (e) { 
 
       node[$PARENT].insertBefore(e, node); 
 
       return this; 
 
      }, 
 
      wrap: function (e) { 
 
       e.appendChild(self.split().after(e).node); 
 
      } 
 
     }; 
 

 
     return self; 
 
    } 
 

 

 
    function select(start, end) { 
 
     return promise(function (resolve) { 
 
      start = wrap(start.text, start.offset, NIL), end = wrap(end.text, NIL, end.offset); 
 
      var selected = []; 
 
      while (start) { 
 
       if (start.is(end)) { 
 
        selected.push(start.to(end)); 
 
        break; 
 
       } 
 
       selected.push(start.toLast()); 
 
       start = start.next(); 
 
      } 
 
      resolve(selected); 
 
     }); 
 
    } 
 

 
    function merge(filter) { 
 
     return function (parts) { 
 
      var result = [parts.shift()]; 
 
      while (parts.length) { 
 
       var prev = result.pop(); 
 
       var next = parts.shift(); 
 
       if (filter(prev.node, next.node)) { 
 
        result.push(next.merge(prev)); 
 
       } else { 
 
        result.push(prev); 
 
        result.push(next); 
 
       } 
 
      } 
 
      return result; 
 
     } 
 
    } 
 

 
    function filter(test) { 
 
     return function (parts) { 
 
      return parts.filter(function (part) { 
 
       return test(part.node); 
 
      }); 
 
     } 
 
    } 
 

 
    function apply(consume) { 
 
     return function (parts) { 
 
      return parts.forEach(function (part) { 
 
       return consume(part); 
 
      }); 
 
     } 
 
    } 
 

 
    var exports = { 
 
     __esModule: true, 
 
     default: select, 
 
     select: select, 
 
     merge: merge, 
 
     filter: filter, 
 
     apply: apply 
 
    }; 
 
    if (bind)for (var name in exports)context[name] = exports[name]; 
 
    return exports; 
 
}); 
 

 

 
(function() { 
 
    var COMPONENT_ID = 'highlight-' + +new Date; 
 
    var highlighter = { 
 
     init: function() { 
 
      this.bindEvents(); 
 
     }, 
 
     /** 
 
     * 
 
     */ 
 
     bindEvents: function() { 
 
      var self = this; 
 
      $('.swatch').on('click', function() { 
 
       $('.swatch').removeClass('active'); 
 
       $(this).addClass('active'); 
 
      }); 
 
      $('.content').mouseup(function() { 
 
       var current = self.actived(); 
 
       if (current.hasClass('clear')) { 
 
        self.clear(); 
 
       } else { 
 
        self.highlight(); 
 
       } 
 
      }); 
 

 
     }, 
 
     actived: function() { 
 
      return $('.swatch.active'); 
 
     }, 
 
     color: function() { 
 
      return this.actived().css('background-color'); 
 
     }, 
 
     /** 
 
     * 
 
     */ 
 
     highlight: function() { 
 
      var self = this; 
 
      var selection = self.getSelection(); 
 
      if (selection) { 
 
       self.select(selection.getRangeAt(0)).// 
 
       then(merge(function (left, right) { 
 
        var p1 = left.parentNode; 
 
        var p2 = right.parentNode; 
 

 
        var a1 = self.compare(left); 
 
        var a2 = self.compare(right); 
 
        return (a1 && a2 && p1.parentNode == p2.parentNode) || 
 
         (!a1 && !a2 && p1 == p2) || 
 
         (a1 && !a2 && p1.parentNode == p2) || 
 
         (!a1 && a2 && p2.parentNode == p1); 
 
       })).then(filter(function (part) { 
 
        return !self.compare(part); 
 
       })).then(function (parts) { 
 
        parts.map(function (node) { 
 
         node.wrap(self.component()); 
 
        }); 
 
       }).catch(function (e) { 
 
        console.log(e); 
 
       }); 
 
       selection.removeAllRanges(); 
 
      } 
 
     }, 
 
     component: function() { 
 
      return $('<span data-toggle="' + COMPONENT_ID + '">').css('background-color', this.color()).get(0); 
 
     }, 
 
     compare: function (text) { 
 
      var self = this; 
 
      var parent = $(text).parent(); 
 
      var highlighted = parent.is(self.selector()); 
 
      var color = parent.css('background-color'); 
 
      return highlighted && color == self.color(); 
 
     }, 
 
     selector: function() { 
 
      return '[data-toggle="?"]'.replace(/\?/, COMPONENT_ID); 
 
     }, 
 
     clear: function() { 
 
      var self = this; 
 
      var selection = self.getSelection(); 
 
      if (selection) { 
 
       self.select(selection.getRangeAt(0)).then(apply(function (part) { 
 
        var text = $(part.split().node); 
 
        while (true) { 
 
         var comp = text.closest(self.selector()); 
 
         if (!comp || !comp.length) { 
 
          break; 
 
         } 
 
         var children = comp.contents(); 
 
         var first = children[0], last = children[children.length - 1]; 
 
         if (text.is(last)) { 
 
          comp.after(text); 
 
         } else if (text.is(first)) { 
 
          comp.before(text); 
 
         } else { 
 
          var heading = comp.clone().empty(); 
 
          for (var i = 0; i < children.length; i++) { 
 
           if (text.is(children[i])) { 
 
            break; 
 
           } 
 
           heading.append(children[i]); 
 
          } 
 
          comp.before(heading).before(text); 
 
         } 
 

 
         if (first == last) comp.remove(); 
 
        } 
 
       })); 
 
       selection.removeAllRanges(); 
 
      } 
 
     }, 
 
     select: function (range) { 
 
      return select(
 
       {text: range.startContainer, offset: range.startOffset}, 
 
       {text: range.endContainer, offset: range.endOffset} 
 
      ); 
 
     }, 
 
     getSelection: function() { 
 
      var sel = window.getSelection(); 
 
      return /^\s*$/.test(self && sel.toString()) ? null : sel; 
 
     } 
 
    }; 
 

 
    highlighter.init(); 
 

 
})();
body { 
 
    margin: 0; 
 
    background: #fafafa; 
 
    box-shadow: 0 0 5rem rgba(0, 0, 0, 0.25) inset; 
 
} 
 

 
::-moz-selection { 
 
    background-color: rgba(0, 0, 0, 0.2); 
 
} 
 

 
::selection { 
 
    background-color: rgba(0, 0, 0, 0.2); 
 
} 
 

 
.content { 
 
    padding: 100px; 
 
} 
 

 
.footer { 
 
    padding: 0 100px 0 100px; 
 
    flex-basis: 100%; 
 
    height: 60px; 
 
    background: #292B2C; 
 
    position:fixed;top:0;width:100%; 
 
} 
 

 
.footer .items-left { 
 
    float: left; 
 
} 
 

 
.footer-item { 
 
    line-height: 60px; 
 
} 
 

 
#colors { 
 
    padding: 12px; 
 
} 
 

 
.swatch { 
 
    width: 30px; 
 
    height: 30px; 
 
    border-radius: 15px; 
 
    box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 2px 2px rgba(0, 0, 0, 0.5); 
 
    display: inline-block; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<div class="content"> 
 
    <span style="color:red;"><b>Content</b> <i>Lorem</i> <font size='7'>ipsum</font> dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 
 
    dolore magna aliqua. Ut enim ad minim</span> veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea 
 
    commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 
 
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est 
 
    laborum. 
 
    Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 
 
    dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea 
 
    commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 
 
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est 
 
    laborum. 
 
    Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 
 
    dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea 
 
    commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 
 
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est 
 
    laborum. 
 
    Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 
 
    dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea 
 
    commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 
 
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est 
 
    laborum. 
 
</div> 
 
<div class="footer"> 
 
    <div class="items-left"> 
 
     <div id="colors"> 
 
      <div class="swatch active" style="background-color: rgba(255,255,131,.5);"></div> 
 
      <div class="swatch" style="background-color: rgba(255,140,218,.5);"></div> 
 
      <div class="swatch" style="background-color: rgba(144,255,184,.5);"></div> 
 
      <div class="swatch clear"></div> 
 
     </div> 
 
    </div> 
 
</div>

+0

私はそれが他の人が私を助けたように助けてくれることを願って!本当にありがとう! –

関連する問題