Problem/Motivation

Extra space is getting added when the next character is not alphanumeric. See image below
sample image

My solution, for now, is to copy glossify-link.html.twig to my custom theme and then remove the extra line.

I'm not sure if there are other ways to resolve this.

CommentFileSizeAuthor
Screenshot 2023-05-30 at 4.57.52 PM.png90.14 KBmjgruta

Issue fork glossify-3363487

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mjgruta created an issue. See original summary.

jacobupal’s picture

I've deleted the linebreak from my copy of glossify-link.html.twig / glossify-tooltip.html.twig yet there is still whitespace between the term and any punctuation, is there anything else you did to fix this?

jacobupal’s picture

This looks promising: Better White Space Control in Twig Templates... but I don't know how that would be implemented.

EDIT:

I think the whitespace is introduced by Html::serialize($html_dom) on line 199 of GlossifyBase.php a process which by default inserts space between the strings being recombined, and if I'm understanding correctly, it's by then just a part of the document, and is the same reason similar whitespace is visible all over the page when you inspect source.

The only solution I've got to work is remove the whitespace afterwards with javascript:

(function (Drupal) {
  Drupal.behaviors.despacer = {
    attach: function (context, settings) {

      // Define the function which removes whitespace-only nodes
      function despace(node) {
        for (var n = 0; n < node.childNodes.length; n++) {
          var child = node.childNodes[n];
          if (child.nodeType === 3 && !/\S/.test(child.nodeValue)) {
            node.removeChild(child);
            n--;
          } else if (child.nodeType === 1) {
            despace(child);
          }
        }
      }

      // Define the relevant parts of the DOM
      var targetElements = context.querySelectorAll(".despace-self, .despace-parent");

      // Call despace function based on conditions
      targetElements.forEach(function(element) {
        if (element.classList.contains('despace-parent')) {
          var parentElement = element.parentElement;
          if (parentElement) {
            despace(parentElement);
          }
        } else if (element.classList.contains('despace-self')) {
          despace(element);
        }
      });
    }
  };
})(Drupal);

To use this I added it to my theme, and adapted the glossify twig template to include the despace-parent class.

jacobupal’s picture

OK, my javascript hack above works until... two glossary terms appear next to each-other, in which case the intended whitespace between them is deleted. So annoying... is there any other solution for this?

jacobupal’s picture

OK it's clearly a hack within a hack but this is my updated script:

(function (Drupal) {
  Drupal.behaviors.my_themeDespacer = {
    attach: function (context, settings) {

      // Define the function which removes whitespace-only nodes
      function despace(node) {
        for (var n = 0; n < node.childNodes.length; n++) {
          var child = node.childNodes[n];
          if (child.nodeType === 3 && !/\S/.test(child.nodeValue)) {
            node.removeChild(child);
            n--;
          } else if (child.nodeType === 1) {
            despace(child);
          }
        }
      }

      // Define the relevant parts of the DOM
      var targetElements = context.querySelectorAll(".despace-self, .despace-parent");

      // Call despace function based on conditions
      targetElements.forEach(function(element) {
        if (element.classList.contains('despace-parent')) {
          var parentElement = element.parentElement;
          if (parentElement) {
            despace(parentElement);
          }
        } else if (element.classList.contains('despace-self')) {
          despace(element);
        }
      });

      // Add a space between .glossify-definition definition elements and the following abbr element
      var definitionElements = context.querySelectorAll(".glossify-definition + abbr");
      definitionElements.forEach(function(element) {
        var prevElement = element.previousElementSibling;
        if (prevElement && prevElement.classList.contains('glossify-definition')) {
          var spaceNode = document.createTextNode(' ');
          prevElement.parentNode.insertBefore(spaceNode, element);
        }
      });
    }
  };
})(Drupal);

It requires that the twig template be something like this:

<abbr tabindex="0" {% if tip %}aria-describedby="{{ word|clean_id }}-definition"{% endif %} class="glossify-tooltip-tip despace-parent glossary-word" data-glossary-id="{{ word|clean_id }}">{{ word }}</abbr>
<span id="{{ word|clean_id }}-definition" class="glossify-definition" style="display: none;">
  <dfn><a href="/read/glossary#{{ word|clean_id }}">{{ word }}</a>: </dfn>
  <span>{{ tip }}</span>
</span>

Your mileage may vary!

jacobupal’s picture

I haven't tried it yet but it looks like this 'spaceless' option in twig could be a useful alternative that would let me do away with using javascript to do this: https://twig.symfony.com/doc/3.x/filters/spaceless.html

tgoeg’s picture

I think the whitespace is introduced by Html::serialize($html_dom) on line 199 of GlossifyBase.php a process which by default inserts space between the strings being recombined

I don't think that's correct. $html_dom or even $word before already has that extra space.
If you enable twig debugging, that is.

At least that's the problem in my case. Disabling debug mode makes the problem go away.

It is caused by the ending tag


<!-- END OUTPUT from 'modules/contrib/glossify/templates/glossify-link.html.twig' -->

.
And I explicitly added a newline here, before the comment, as that is what Drupal core does as well.
This very newline causes the whitespace to be inserted.

The following patch for Drupal core fixes it for me (as it does in several other cases where debug mode manipulates the DOM in such a way that it causes differences in rendering.

--- /core/themes/engines/twig/twig.engine.org   2024-05-08 16:37:50.990994524 +0000
+++ /core/themes/engines/twig/twig.engine   2024-05-08 16:38:47.900186459 +0000
@@ -63,8 +63,8 @@
     throw $e;
   }
   if ($twig_service->isDebug()) {
-    $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
-    $output['debug_prefix'] .= "\n<!-- THEME HOOK: '" . Html::escape($variables['theme_hook_original']) . "' -->";
+    $output['debug_prefix'] .= "<!-- THEME DEBUG -->";
+    $output['debug_prefix'] .= "<!-- THEME HOOK: '" . Html::escape($variables['theme_hook_original']) . "' -->";
     // If there are theme suggestions, reverse the array so more specific
     // suggestions are shown first.
     if (!empty($variables['theme_hook_suggestions'])) {
@@ -106,17 +106,17 @@
         $prefix = ($template == $current_template) ? 'x' : '*';
         $suggestion = $prefix . ' ' . $template;
       }
-      $output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n   " . Html::escape(implode("\n   ", $suggestions)) . "\n-->";
+      $output['debug_info'] .= "<!-- FILE NAME SUGGESTIONS:\n   " . Html::escape(implode("\n   ", $suggestions)) . "-->";

       if (!empty($invalid_suggestions)) {
-        $output['debug_info'] .= "\n<!-- INVALID FILE NAME SUGGESTIONS:";
-        $output['debug_info'] .= "\n   See https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Render!theme.api.php/function/hook_theme_suggestions_alter";
-        $output['debug_info'] .= "\n   " . Html::escape(implode("\n   ", $invalid_suggestions));
-        $output['debug_info'] .= "\n-->";
+        $output['debug_info'] .= "<!-- INVALID FILE NAME SUGGESTIONS:";
+        $output['debug_info'] .= "   See https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Render!theme.api.php/function/hook_theme_suggestions_alter";
+        $output['debug_info'] .= "   " . Html::escape(implode("\n   ", $invalid_suggestions));
+        $output['debug_info'] .= "-->";
       }
     }
-    $output['debug_info']   .= "\n<!-- BEGIN OUTPUT from '" . Html::escape($template_file) . "' -->\n";
-    $output['debug_suffix'] .= "\n<!-- END OUTPUT from '" . Html::escape($template_file) . "' -->\n\n";
+    $output['debug_info']   .= "<!-- BEGIN OUTPUT from '" . Html::escape($template_file) . "' -->";
+    $output['debug_suffix'] .= "<!-- END OUTPUT from '" . Html::escape($template_file) . "' -->";
   }
   // This output has already been rendered and is therefore considered safe.
   return Markup::create(implode('', $output));

Adding this to #2914733: [META] twig debug can break parts of site as well.

jacobupal’s picture

Good to hear! This might have changed through updates since I first resorted to javascript, as I am pretty sure the problem was persisting when twig-debug was disabled.

I am looking forward to trying that patch!

anybody’s picture

Version: 3.0.0 » 3.x-dev
Status: Active » Needs work

Hi and thank you for the report. It's not yet clear to me, what needs to happen here and how to reproduce this. Could you update the issue summary with clear steps please?

So is this similar to: #3251765: Twig debug mode causes parse error in appendXML just with another effect?
Or is an implementation of https://twig.symfony.com/doc/3.x/filters/spaceless.html needed in the twig file?

Happy to review fixes as MR.

tgoeg’s picture

I don't know if you addressed me, but IMHO the problem lies within core's way of inserting debugging comments itself.

There is an ongoing debate of whether enabling debug mode can be expected to give the same (rendered) results as when disabling it. And some core devs seem to be reluctant to accept any changes regarding the current markup as a site is expected to possibly fail when debugging is enabled.

I'd change core's debugging functionality as I showed above, as it doesn't hurt but fixes a few issues (on my side at least).

Depending on whether this is the way to go there is either nothing to do here or some additional workaround has to be implemented to get rid of those extraneous newlines.
Although I don't see #3251765: Twig debug mode causes parse error in appendXML in my installation, it seems very much related and both ways of fixing it outlined above may be a possible solution for it as well.

tgoeg’s picture

I found an additional solution in my notes.

--- /modules/contrib/glossify/templates/glossify-link.html.twig    2023-05-08 12:03:53.000000000 +0000
+++ /modules/contrib/glossify/templates/glossify-link.html.twig    2024-05-08 16:54:06.872025951 +0000
@@ -11,4 +11,4 @@
  * @ingroup themeable
  */
 #}
-<a href="{{ tipurl }}" {% if tip %}title="{{ tip }}"{% endif %} class="glossify-tooltip-link">{{ word }}</a>
+<a href="{{ tipurl }}" {% if tip %}title="{{ tip }}"{% endif %} class="glossify-tooltip-link">{{ word }}</a>{# this or any comment here removes the erroneous newline that causes spaces when enabling twig debugging, noticeable when terms are right before non-blank characters like punctuation - see https://www.drupal.org/project/glossify/issues/3363487 #}

However, I cannot tell you how or why this works.
I definitely had a bright moment when implementing this :-)

This is needed additionally to the fix in core mentioned above for this edge case (the very bug OP describes; sorry, we drifted away to more general cases).
The fact that debugging mode does not lead to problems when terms are followed by space characters is caused by the browser reducing multiple space chars to one.

jacobupal’s picture

I can confirm now that on a clean install of 10.3.x using DrupalPod that toggling twig debugging (through the UI/config) does indeed result in that extra space appearing and disappearing.

So the problem, as it exists now, is not with glossify but with the twig debugging implementation, just as you suggested @tgoeg

Any line-break issues I am able to reproduce now while the twig-debugging is turned off are associated with my custom template, so again, not strictly glossify's problem.

I do think that there were initially factors other than twig-debugging at-play earlier on in the timeline - both because in @mjgruta'a OP we can see their devtools in the screenshot, and there's no debugging markup there, and because they seem to have been saying that when they deleted the empty line (which is part of Drupal's coding standards) the white-space went away, which wouldn't make sense if the cause had always been twig debugging.

Also, that same solution just over 2 months later, presumably with a different drupal version, didn't work for me in 2023.

I have the impression that some thing(s) outside of the module, in twig or in core, have been changed or improved since this issue was created, leading to some of our confusion. And it'd be worth keeping an eye on anything in core which might affect this.

If this does come back again for anybody else who ends up down the same path as me, I can say that the whitespace went away (with debugging off) when I did any of the following:

  • Deleted my custom template and fell back to the default glossify-link.html.twig
  • Made sure my custom glossify-link.html.twig only contained nested elements.
  • Made sure my custom glossify-link.html.twig had a wrapper element with no whitespace between the nested elements.
  • Used the twig spaceless filter to achieve something similar. (I know the spaceless filter is due to be deprecated, but I wanted to see how it works. Turns out it only worked on spaces introduced by my template - the spaces introduced by twig debugging mode were untouched which makes sense. )
  • As a last resort used javascript to remove the unwanted whitespace.

Sadly including the extra comment as suggested by @tgoeg made no difference to the extra whitespace when debugging was turned on.

But if it's all working now, apart from the debugging issue, maybe deserves its own issue in core, and there is little to be done on this issue?

grevil’s picture

Status: Needs work » Postponed (maintainer needs more info)

Is this still an issue on the latest dev release?

tgauges’s picture