Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import $ from 'jquery';
- import join from 'lodash/join';
- import slice from 'lodash/slice';
- import isNil from 'lodash/isNil';
- /**
- * Mutate the contents of an HTML node if it is not within the bounds of a
- * parent node by truncating the output.
- *
- * If the content of the `container` is outside of the parent's bounds
- * (overflow is set to hidden for example), this function will mutate the
- * `container` HTML such that it has an ellipsis on the last visible word.
- *
- * Some caveats:
- *
- * - If all of the text fits then no mutations are made to the HTML.
- * - If none of the text is visible, it is removed from the display.
- *
- * @param {DOM Node | jQuery obj} parent - A parent should wrap the target node
- * where the contents to be truncated
- * exists. Its position on the page is
- * the bounding box used to check if
- * any text overflows.
- *
- * @param {DOM Node | jQuery obj} container - The container is the target node
- * which contains the text to be
- * truncated. It should contain just
- * text nodes.
- *
- * @return void
- */
- export default (parent, container) => {
- const $container = $(container);
- const originalHtml = $container.html();
- // build a new HTML string such that all words are wrapped in a <span>.
- $container.html(`<span>${$container.text().replace(/\s+/g, '</span> <span>')}</span>`);
- const wordNodes = [...$container.find('span')];
- const lastVisibleWordIdx = findLastVisibleWordIdx(wordNodes, parent);
- // if we can't display a single word, make it empty
- if (isNil(lastVisibleWordIdx)) {
- $container.html('');
- // all the text fits, hooray!
- } else if (lastVisibleWordIdx + 1 === wordNodes.length) {
- $container.html(originalHtml);
- // we need to truncate the string so we build a new slice and append the
- // ellipsis to the end to show it has been cut off.
- } else {
- $container.html(`${join(slice(originalHtml.split(/\s+/), 0, lastVisibleWordIdx), ' ')}...`);
- }
- };
- /**
- * Based on a list of word nodes, find the last visible node within the
- * parent's bounds in a page. We use binary search to make this much faster in
- * the case that there is a lot of text to look at.
- *
- * @param {array} wordNodes - A list of DOM nodes which contain text.
- * @param {DOM Node | jQquery obj} parent - the parent node wrapping all of the word nodes.
- *
- * @return {int} the last visible word index
- *
- */
- const findLastVisibleWordIdx = (wordNodes, parent) => {
- const $parent = $(parent);
- var minIdx = 0;
- var maxIdx = wordNodes.length - 1;
- while (maxIdx >= minIdx) {
- var currentIdx = Math.floor((minIdx + maxIdx) / 2);
- const $currentWord = $(wordNodes[currentIdx]);
- const $nextWord = $(wordNodes[currentIdx + 1]);
- const isCurrentNodeVisible = $currentWord.length && isWordVisible($currentWord, $parent);
- const isNextNodeVisible = $nextWord.length && isWordVisible($nextWord, $parent);
- if (isCurrentNodeVisible && !isNextNodeVisible) {
- return currentIdx;
- } else if (isCurrentNodeVisible && isNextNodeVisible) {
- minIdx = currentIdx + 1;
- } else {
- maxIdx = currentIdx - 1;
- }
- }
- return undefined;
- };
- /**
- * Check if a word is visible within a parent's bounds
- *
- * @param {jQuery obj} $word - the actual word jQuery object to check
- * @param {jQuery obj} $parent - the parent node which contains the word node
- *
- * @return {bool} whether the node is in the parent's bounds
- *
- */
- const isWordVisible = ($word, $parent) => {
- const parentBottom = $parent.offset().top + $parent.outerHeight();
- const parentLeft = $parent.offset().left;
- const parentRight = $parent.offset().left + $parent.outerWidth();
- const nodeBottom = $word.offset().top + $word.height();
- const nodeLeft = $word.offset().left;
- const nodeRight = nodeLeft + $word.width();
- if(nodeBottom > parentBottom || nodeLeft < parentLeft || nodeRight > parentRight) {
- return false;
- }
- return true;
- };
Add Comment
Please, Sign In to add comment