Guest User

Untitled

a guest
May 21st, 2018
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.08 KB | None | 0 0
  1. import $ from 'jquery';
  2.  
  3. import join from 'lodash/join';
  4. import slice from 'lodash/slice';
  5. import isNil from 'lodash/isNil';
  6.  
  7. /**
  8. * Mutate the contents of an HTML node if it is not within the bounds of a
  9. * parent node by truncating the output.
  10. *
  11. * If the content of the `container` is outside of the parent's bounds
  12. * (overflow is set to hidden for example), this function will mutate the
  13. * `container` HTML such that it has an ellipsis on the last visible word.
  14. *
  15. * Some caveats:
  16. *
  17. * - If all of the text fits then no mutations are made to the HTML.
  18. * - If none of the text is visible, it is removed from the display.
  19. *
  20. * @param {DOM Node | jQuery obj} parent - A parent should wrap the target node
  21. * where the contents to be truncated
  22. * exists. Its position on the page is
  23. * the bounding box used to check if
  24. * any text overflows.
  25. *
  26. * @param {DOM Node | jQuery obj} container - The container is the target node
  27. * which contains the text to be
  28. * truncated. It should contain just
  29. * text nodes.
  30. *
  31. * @return void
  32. */
  33. export default (parent, container) => {
  34. const $container = $(container);
  35. const originalHtml = $container.html();
  36.  
  37. // build a new HTML string such that all words are wrapped in a <span>.
  38. $container.html(`<span>${$container.text().replace(/\s+/g, '</span> <span>')}</span>`);
  39.  
  40. const wordNodes = [...$container.find('span')];
  41. const lastVisibleWordIdx = findLastVisibleWordIdx(wordNodes, parent);
  42.  
  43. // if we can't display a single word, make it empty
  44. if (isNil(lastVisibleWordIdx)) {
  45. $container.html('');
  46.  
  47. // all the text fits, hooray!
  48. } else if (lastVisibleWordIdx + 1 === wordNodes.length) {
  49. $container.html(originalHtml);
  50.  
  51. // we need to truncate the string so we build a new slice and append the
  52. // ellipsis to the end to show it has been cut off.
  53. } else {
  54. $container.html(`${join(slice(originalHtml.split(/\s+/), 0, lastVisibleWordIdx), ' ')}...`);
  55.  
  56. }
  57. };
  58.  
  59.  
  60. /**
  61. * Based on a list of word nodes, find the last visible node within the
  62. * parent's bounds in a page. We use binary search to make this much faster in
  63. * the case that there is a lot of text to look at.
  64. *
  65. * @param {array} wordNodes - A list of DOM nodes which contain text.
  66. * @param {DOM Node | jQquery obj} parent - the parent node wrapping all of the word nodes.
  67. *
  68. * @return {int} the last visible word index
  69. *
  70. */
  71. const findLastVisibleWordIdx = (wordNodes, parent) => {
  72. const $parent = $(parent);
  73.  
  74. var minIdx = 0;
  75. var maxIdx = wordNodes.length - 1;
  76.  
  77. while (maxIdx >= minIdx) {
  78. var currentIdx = Math.floor((minIdx + maxIdx) / 2);
  79.  
  80. const $currentWord = $(wordNodes[currentIdx]);
  81. const $nextWord = $(wordNodes[currentIdx + 1]);
  82.  
  83. const isCurrentNodeVisible = $currentWord.length && isWordVisible($currentWord, $parent);
  84. const isNextNodeVisible = $nextWord.length && isWordVisible($nextWord, $parent);
  85.  
  86. if (isCurrentNodeVisible && !isNextNodeVisible) {
  87. return currentIdx;
  88. } else if (isCurrentNodeVisible && isNextNodeVisible) {
  89. minIdx = currentIdx + 1;
  90. } else {
  91. maxIdx = currentIdx - 1;
  92. }
  93. }
  94.  
  95. return undefined;
  96. };
  97.  
  98.  
  99. /**
  100. * Check if a word is visible within a parent's bounds
  101. *
  102. * @param {jQuery obj} $word - the actual word jQuery object to check
  103. * @param {jQuery obj} $parent - the parent node which contains the word node
  104. *
  105. * @return {bool} whether the node is in the parent's bounds
  106. *
  107. */
  108. const isWordVisible = ($word, $parent) => {
  109. const parentBottom = $parent.offset().top + $parent.outerHeight();
  110. const parentLeft = $parent.offset().left;
  111. const parentRight = $parent.offset().left + $parent.outerWidth();
  112.  
  113. const nodeBottom = $word.offset().top + $word.height();
  114. const nodeLeft = $word.offset().left;
  115. const nodeRight = nodeLeft + $word.width();
  116.  
  117. if(nodeBottom > parentBottom || nodeLeft < parentLeft || nodeRight > parentRight) {
  118. return false;
  119. }
  120.  
  121. return true;
  122. };
Add Comment
Please, Sign In to add comment