function customSelect(target, onSelect) {
var oldHTML = target.innerHTML;
// this is the regex that wraps the words with spans:
target.innerHTML = oldHTML.replace(/[\d\w']+[\s,.]*/g, '<span>$&</span>');
var spans = target.querySelectorAll('span');
// I used a basic blue/white style, but you can change it in the CSS
// using the ".text-selected" selector
var alreadySelected = [];
var setSpanOn = function(span) {
alreadySelected.push(span);
span.className = 'text-selected';
};
var setSpanOff = function(span) {
span.className = '';
};
// here starts the logic
var isSelecting = false;
for (var i=0, l=spans.length; i<l; i++) {
(function span_handlers(span, pos) {
// when the user starts holding the mouse button
span.onmousedown = function() {
// deselect previous selection, if any:
alreadySelected.splice(0).forEach(setSpanOff);
// and enable selection:
isSelecting = true;
span.onmouseenter();
};
// the main logic, we check if we need to set or not this span as selected:
span.onmouseenter = function() {
if (!isSelecting)
return;
// if already selected
var j = alreadySelected.indexOf(span);
if (j >= 0) {
// then deselect the spans that were selected after this span
alreadySelected.splice(j+1).forEach(setSpanOff);
}
else {
// else if is not the first, check if the user selected another word
// one line down or up. This is done by checking the indexes:
if (alreadySelected.length) {
var last = alreadySelected[alreadySelected.length-1];
var posLast = [].indexOf.call(spans, last);
var typeSibling = pos > posLast ? 'nextSibling' : 'previousSibling';
while (1) {
last = last[typeSibling];
if (last !== span)
setSpanOn(last);
else break;
}
}
setSpanOn(span);
}
};
// when the user hold up the mouse button:
span.onmouseup = function() {
isSelecting = false;
// call the onSelect function passing the selected spans content:
if (typeof onSelect === 'function') {
var spansSelected = target.querySelectorAll('.text-selected');
var text = [].map.call(spansSelected, function(span) {
return span.textContent || '';
}).join('').trim();
onSelect(text);
}
};
})(spans[i], i);
}
};
// Usage:
var element = document.getElementById('target');
var onSelect = function(text) {
console.log(text);
};
customSelect(element, onSelect);
#target {
user-select: none;
}
.text-selected {
background-color: blue;
color: white;
}
<div id="target">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>