is-in-text-block.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import getComposedParent from './get-composed-parent';
  2. import sanitize from '../text/sanitize';
  3. import { getNodeFromTree } from '../../core/utils';
  4. function walkDomNode(node, functor) {
  5. if (functor(node.actualNode) !== false) {
  6. node.children.forEach(child => walkDomNode(child, functor));
  7. }
  8. }
  9. const blockLike = [
  10. 'block',
  11. 'list-item',
  12. 'table',
  13. 'flex',
  14. 'grid',
  15. 'inline-block'
  16. ];
  17. function isBlock(elm) {
  18. var display = window.getComputedStyle(elm).getPropertyValue('display');
  19. return blockLike.includes(display) || display.substr(0, 6) === 'table-';
  20. }
  21. function getBlockParent(node) {
  22. // Find the closest parent
  23. let parentBlock = getComposedParent(node);
  24. while (parentBlock && !isBlock(parentBlock)) {
  25. parentBlock = getComposedParent(parentBlock);
  26. }
  27. return getNodeFromTree(parentBlock);
  28. }
  29. /**
  30. * Determines if an element is within a text block
  31. * @param {Element} node [description]
  32. * @return {Boolean} [description]
  33. */
  34. function isInTextBlock(node) {
  35. if (isBlock(node)) {
  36. // Ignore if the link is a block
  37. return false;
  38. }
  39. // Find all the text part of the parent block not in a link, and all the text in a link
  40. const virtualParent = getBlockParent(node);
  41. let parentText = '';
  42. let linkText = '';
  43. let inBrBlock = 0;
  44. // We want to ignore hidden text, and if br / hr is used, only use the section of the parent
  45. // that has the link we're looking at
  46. walkDomNode(virtualParent, currNode => {
  47. // We're already passed it, skip everything else
  48. if (inBrBlock === 2) {
  49. return false;
  50. }
  51. if (currNode.nodeType === 3) {
  52. // Add the text to the parent
  53. parentText += currNode.nodeValue;
  54. }
  55. // Ignore any node that's not an element (or text as above)
  56. if (currNode.nodeType !== 1) {
  57. return;
  58. }
  59. var nodeName = (currNode.nodeName || '').toUpperCase();
  60. // BR and HR elements break the line
  61. if (['BR', 'HR'].includes(nodeName)) {
  62. if (inBrBlock === 0) {
  63. parentText = '';
  64. linkText = '';
  65. } else {
  66. inBrBlock = 2;
  67. }
  68. // Don't walk nodes with content not displayed on screen.
  69. } else if (
  70. currNode.style.display === 'none' ||
  71. currNode.style.overflow === 'hidden' ||
  72. !['', null, 'none'].includes(currNode.style.float) ||
  73. !['', null, 'relative'].includes(currNode.style.position)
  74. ) {
  75. return false;
  76. // Don't walk links, we're only interested in what's not in them.
  77. } else if (
  78. (nodeName === 'A' && currNode.href) ||
  79. (currNode.getAttribute('role') || '').toLowerCase() === 'link'
  80. ) {
  81. if (currNode === node) {
  82. inBrBlock = 1;
  83. }
  84. // Grab all the text from this element, but don't walk down it's children
  85. linkText += currNode.textContent;
  86. return false;
  87. }
  88. });
  89. parentText = sanitize(parentText);
  90. linkText = sanitize(linkText);
  91. return parentText.length > linkText.length;
  92. }
  93. export default isInTextBlock;