7

I added an image to the Trix editor, generating the following code:

<figure 
    data-trix-attachment="{lots of data}" 
    data-trix-content-type="image/jpeg"
    data-trix-attributes="{'presentation':'gallery'}" 
    class="attachment attachment--preview attachment--jpg">
    <img src="http://myhost/myimage.jpg" width="5731" height="3821">
    <figcaption class="attachment__caption">
        <span class="attachment__name">cool.jpg</span> <span class="attachment__size">4.1 MB</span>
    </figcaption>
</figure>

When I display the generated HTML from the editor on my Bootstrap-based page, the image obviously extends the screen (see the width and height) and I'd like to remove these props and also assign the img-fluid class to it.

So basically I thought to use the config:

Trix.config.css.attachment = 'img-fluid'

But that does a) not change the attachment class to img-fluid and it also would not apply the changes to the image but the figure.

I would like to avoid using jQuery each time I display the content and traverse all figures and then manipulate the image's properties at runtime.

Isn't there a solution to define these styles when adding the attachment?

Jankapunkt
  • 8,128
  • 4
  • 30
  • 59

1 Answers1

10

Trix does not have any kind of support to change the image element inside the attachment. One way to do it is by using MutationObserver to check for mutations inside Trix editor that apply to attributes, childList and subtree.

If we have a width or height attributes mutation to an img target node with a figure parent node, then we remove those attributes and we can apply the class img-fluid to the first attribute mutation, for example width.

Run code snippet and try to add some image attachments to see or inspect the HTML

Please read inline comments

// Listen to trix-attachment-add event so we'll get rid of the progress bar just for this demo
// Here we should upload the attachment and handle progress properly
document.addEventListener("trix-attachment-add", event => {
  const { attachment } = event.attachment;
 
  // Get rid of the progress bar
  attachment.setUploadProgress(100)
});


// Get the Trix editor
const editor = document.querySelector('trix-editor');

// Instantiating an observer
const observer = new MutationObserver(function (mutations) {
  mutations.forEach(({ type, target, attributeName }) => {
    
    // If the parent is a figure with an img target
    if (target.parentNode.tagName === 'FIGURE' && 
        target.nodeName === 'IMG')
    {
      if (type === 'attributes') {
        switch(attributeName) {

          // If we have attribute width
          case 'width':
            // Remove attribute width
            target.removeAttribute('width');
            // Add img-fluid only once
            target.classList.add('img-fluid');
            break;

          // If we have attribute height
          case 'height':
            // Remove attribute height
            target.removeAttribute('height');
            break;
        }
      }

      // Render images HTML code
      renderHtmlOutput();
    }

  });
});

// Observing Trix Editor
observer.observe(editor, {
  attributes: true,
  childList: true,
  subtree: true
});

// Function to render every figure > img HTML code
function renderHtmlOutput() {
  const images = editor.querySelectorAll('figure > img');
  let output = '';

  for(const image of images) {
    output += image.outerHTML.replace(/ /g, "\n  ") + "\n";
  }

  document.getElementById('output-html').textContent = output;
}
body {
  height: 100vh;
  margin: 0;
  flex-direction: column;
  display: flex;
}
#main {
  display: flex;
  flex-direction: row;
  flex: 1;
  margin: 10px;
}

#editor-container {
  flex: 3;
}

#output-container {
  flex: 2;
  margin-left: 20px;
  border-left: 1px solid lightgray;
  overflow: auto;
}

#output-html {
  margin: 0;
  padding: 10px;
  font-size: small;
  color: blue;
}

/* Hide some Trix buttons to free horizontal space */
.trix-button--icon-increase-nesting-level,
.trix-button--icon-decrease-nesting-level,
.trix-button--icon-bullet-list,
.trix-button--icon-number-list { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/trix/1.2.1/trix.js" integrity="sha256-2D+ZJyeHHlEMmtuQTVtXt1gl0zRLKr51OCxyFfmFIBM=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/trix/1.2.1/trix.css" integrity="sha256-yebzx8LjuetQ3l4hhQ5eNaOxVLgqaY1y8JcrXuJrAOg=" crossorigin="anonymous"/>

<section id="main">
  <div id="editor-container">
    <form>
      <input id="editor" value="Editor content goes here" type="hidden" name="content">
      <trix-editor input="editor"></trix-editor>
    </form>
  </div>
  <div id="output-container">
    <pre id="output-html"></pre>
  </div>
</section>
Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
  • can you possibly explain this using react? @christoslytras – ertemishakk Jun 20 '21 at 14:12
  • @ertemishakk there is nothing special using `MutationObserver` in React, you just do it by using refs made with `useRef` hook. If you want to make things easier, there is a hook you can use for `MutationObserver` itself and you can find details about installation and usage here [@rooks/use-mutation-observer](https://www.npmjs.com/package/@rooks/use-mutation-observer). – Christos Lytras Jun 20 '21 at 15:44
  • why does your code adds img-fluid to only first added attachment? @christoslytras – ertemishakk Jun 21 '21 at 10:50
  • @ertemishakk it doesn't add it on the first attachment, it adds it **on one of the attributes of each attachment found**, don't you see what purpose the `switch/case` is there for? It's for **attributes**, it writes `switch(attributeName)` so it's obviously for the image attributes; so, we only add the class `img-fluid` to the **width attribute** because **we want to add it just once** and don't waste CPU cycles on adding twice a class that already exists. – Christos Lytras Jun 21 '21 at 11:22