Search code examples
javascripthtmlcsscontenteditable

Center an image in a contenteditable div


Please before tagging the question as duplicated, I tell you, I've been searching a lot and can't find a clear answer, so it may be worth it to try to get a clear one for the year we are living, 2022.

I have this contenteditable div:

  <div class="input-field" placeholder="Tell me something..." contentEditable="true"></div>

I've been able to center the text and regular emojis setting the line-height in css but when I enter an image I am using as a custom emoji it does not get centered.

<img src="img/emojis/red-circle.png" class="image-emoji">

enter image description here

I am adding the image to the input field with event.target after I click it from another box:

inputField.innerHTML +=
      "<div class='emo'>" + event.target.outerHTML + "</div>";

But the Divs get removed by innerHTML, so it blocks me from adding display:flex; align-items: center;

And if I set display:flex; align-items: center; directly on the contenteditable I get horizontal scrolling which I do not want.

Then if I set the css to be display:flex; align-items: center; flex-direction: column; on the contenteditable the image-emojis display one on each line every time, not side by side.

enter image description here

Please help.

EDIT:

Following the first answer advice I tested flex-wrap:wrap; on the contenteditable which also produces horizontal scrolling on long words with no spaces and flex-wrap:nowrap; does the same.

enter image description here

EDIT-2:

As suggested adding break-word:break-all; works partially but now I notice another problem with display: flex; It prevents the ENTER key from adding additional lines even thought <div> <br> </div> are added to the Html, any idea?

EDIT-3

Finally not using flex box, but just line-height to align text and emojis and as per Lukas below:

 .input-field img {
  vertical-align: middle;
}

...aligned the image.

enter image description here

const emojiBox = document.querySelector(".emoji-box");
const inputField = document.querySelector(".input-field");

emojiBox.addEventListener("click", (e) => {
  let emoji = null;
  let isImage = null;
  let emojiImage = null;
  let removeBR = null;
  
  if (e.target != e.currentTarget) {    
    removeBR = inputField.querySelector("br");
    if (removeBR) {
      removeBR.outerHTML = "";
    }

    if (
      e.target.tagName.toLowerCase() === "img" &&
      e.target.classList.value.toLowerCase() === "emoji"
    )
      isImage = true;

    if (isImage) {
      emojiImage = e.target;
    } else {
      emoji = e.target;
    }
  }


  if (emoji) {
    inputField.innerHTML += emoji.outerHTML;
  } else if (emojiImage) {
    inputField.innerHTML += emojiImage.outerHTML;
  }
  cursorAtTheEnd();
});

function cursorAtTheEnd() {
  let sel = window.getSelection();
  sel.selectAllChildren(inputField);
  sel.collapseToEnd();
  inputField.focus();
}
html {
  
  font-size: 62.5%;
 
}

.image-emoji { 
  max-width: 1.8rem;
  max-height: 1.8rem;
  border-radius: 100%;
  padding: 0;
  margin: 0 0.15rem 0;  
}


.input-field {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  word-break: break-all;
   font-size:1.6rem;
  min-height: 3rem;
  max-height: 20rem;
  width: 40rem; 
  margin: 0.5rem;
  padding: 1rem 6rem 1rem 4rem;
  border: 2px solid #e6e6e6;
  border-radius: 0.5rem;
  outline: none;
  overflow-y: auto;

  
}


[contenteditable="true"]:empty:before {  
  content: attr(placeholder);
  pointer-events: none;
  display: block; /* For Firefox */
}


.emoji-box {
  display: flex;
  align-items: center;  
  font-size:1.6rem;
  background: white;
  border: 0.2rem solid #eee;
  border-radius: 0.5rem;
  height: 3rem;
  width: 40rem;  
  padding: 1rem 6rem 1rem 4rem;
  margin: 0.5rem;
  color: #183153;
  cursor: pointer;
  overflow-y: auto;
  
}
<div class="emoji-box">
<span>&#128578;</span>
<img src="https://upload.wikimedia.org/wikipedia/commons/0/02/Red_Circle%28small%29.svg" class="image-emoji">
</div>

<div class="input-field" placeholder="Tell me something..." contentEditable="true"></div>


Solution

  • try this

    const emojiBox = document.querySelector(".emoji-box");
    const inputField = document.querySelector(".input-field");
    
    let lastKeyEnter = false;
    
    function removeBR() {
        let removeBR = inputField.querySelector("br:last-of-type");
    
        if (
            removeBR &&
            removeBR.previousElementSibling &&
            removeBR.previousElementSibling.tagName === "BR"
        ) {
            removeBR.remove();
        }
    }
    
    inputField.onkeydown = (e) => {
        if (e.keyCode === 13) {
            lastKeyEnter = true;
        } else if (lastKeyEnter) {
            lastKeyEnter = false;
        }
    };
    
    emojiBox.addEventListener("click", (e) => {
        let emoji = null;
        let isImage = null;
        let emojiImage = null;
    
        if (e.target != e.currentTarget) {
            if (
                e.target.tagName.toLowerCase() === "img" &&
                e.target.classList.value.toLowerCase() === "emoji"
            )
                isImage = true;
    
            if (isImage) {
                emojiImage = e.target;
            } else {
                emoji = e.target;
            }
        }
    
        let lastChild = inputField.lastChild;
    
        if (
            lastChild &&
            lastChild.previousSibling &&
            lastChild.previousSibling.tagName === undefined
        ) {
            lastChild.tagName === "BR" ? lastChild.remove() : "";
        }
    
        if (emoji && emoji.tagName === "SPAN") {
            lastKeyEnter ? removeBR() : "";
            lastKeyEnter = false;
            inputField.innerHTML += emoji.innerHTML;
        } else if (emoji && emoji.tagName === "IMG") {
            lastKeyEnter ? removeBR() : "";
            lastKeyEnter = false;
            inputField.innerHTML += emoji.outerHTML;
        } else if (emojiImage) {
            lastKeyEnter ? removeBR() : "";
            lastKeyEnter = false;
            inputField.innerHTML += emojiImage.outerHTML;
        }
        cursorAtTheEnd();
    });
    
    function cursorAtTheEnd() {
        let sel = window.getSelection();
        sel.selectAllChildren(inputField);
        sel.collapseToEnd();
        inputField.focus();
    }
    html {
      
      font-size: 62.5%;
     
    }
    
    .image-emoji { 
      max-width: 1.8rem;
      max-height: 1.8rem;
      border-radius: 100%;
      padding: 0;
      margin: 0 0.15rem 0;  
    }
    
    
    .input-field {
      word-break: break-all;
       font-size:1.6rem;
      min-height: 3rem;
      max-height: 20rem;
      width: 40rem; 
      margin: 0.5rem;
      padding: 1rem 6rem 1rem 4rem;
      border: 2px solid #e6e6e6;
      border-radius: 0.5rem;
      outline: none;
      overflow-y: auto;
      display: inline-block;
      line-height: 2rem;
      
    }
    
    .input-field img {
      vertical-align: text-bottom;
    }
    
    
    [contenteditable="true"]:empty:before {  
      content: attr(placeholder);
      pointer-events: none;
      display: block; /* For Firefox */
    }
    
    
    .emoji-box {
      display: flex;
      align-items: center;  
      font-size:1.6rem;
      background: white;
      border: 0.2rem solid #eee;
      border-radius: 0.5rem;
      height: 3rem;
      width: 40rem;  
      padding: 1rem 6rem 1rem 4rem;
      margin: 0.5rem;
      color: #183153;
      cursor: pointer;
      overflow-y: auto;
      
    }
    <div class="emoji-box">
        <span>&#128578;</span>
        <img src="https://upload.wikimedia.org/wikipedia/commons/0/02/Red_Circle%28small%29.svg" class="image-emoji">
    </div>
    
    <div class="input-field" placeholder="Tell me something..." contentEditable="true"></div>