Posição de cursor e altura de seleção para elementos embutidos no Chrome

8

Estou pensando em criar um editor com Slate ou ProseMirror e vendo algum comportamento estranho com o Chrome em torno da posição do cursor e da área de seleção ao usar um elemento embutido. O problema 1 é mostrado na primeira figura abaixo. Quando a posição do cursor de texto está atrás do "f", o cursor é mostrado na parte superior da imagem. O problema 2 está na segunda imagem - selecionar o texto mostra uma área destacada que é tão alta quanto no elemento embutido. Existe alguma maneira de controlar esse comportamento e, em vez disso, mostrar o sinal de intercalação na posição do texto e realçar apenas o espaço ao redor do texto (mesmo se a imagem embutida estiver aumentando a altura da linha) posição de sinal de intercalação muito alta área de seleção muito grande

Eu gostaria de imitar o comportamento aqui, do Firefox: insira a descrição da imagem aqui insira a descrição da imagem aqui

Imagens de exemplo foram produzidas usando a demonstração ProseMirror aqui: https://prosemirror.net/examples/basic/

Um exemplo mínimo (obrigado @Kaiido) com JSBin :

<div contenteditable>Test text<img src="https://upload.wikimedia.org/wikipedia/en/9/9b/Yoda_Empire_Strikes_Back.png">Testing</div>

Não tenho certeza de como isso se comporta em outros sistemas operacionais, mas estou usando o macOS Catalina.

mowwwalker
fonte
2
... Você pode fazer um exemplo reproduzível mínimo com apenas <div contenteditable>Test text<img src="https://upload.wikimedia.org/wikipedia/en/9/9b/Yoda_Empire_Strikes_Back.png">Testing</div>. Agora, é assim que eles fazem isso, atualmente não há especificações sobre isso, então eles são livres para fazer o que acham melhor. Você absolutamente precisa dessas bibliotecas, ou uma solução bruta é suficiente para você?
Kaiido 23/04
1
Apenas uma observação: o TinyMCE parece se comportar de maneira bastante semelhante no Chrome, exceto por esticar o cursor até a altura da imagem.
V.Volkov 23/04
obrigado @Kaiido, irá incluir na pergunta.
mowwwalker 23/04
Eu acho que é relacionado ao sistema operacional. Eu vejo o mesmo comportamento @ V.Volkov apenas descrito no chrome no mac.
Dekel

Respostas:

4

Você pode corrigir o problema de seleção usando o flexbox. Inicialmente, tentei usar, align-items: flex-endmas estava tendo um comportamento estranho com as setas. align-items: baselineparece funcionar a partir de agora.

O problema da imagem é muito estranho. Percebi que o Chrome passa por cima dos elementos SVG de maneira diferente dos elementos IMG. A partir de agora, a versão mais recente do Chrome "espera" antes de pular o IMG, mas permite que o usuário pule um SVG como qualquer outro caractere (a seta esquerda pula o caractere mais próximo do svg). Isso pode ser causado por estilos padrão subjacentes, mas eu não sei.

Eu estava prestes a postar minhas descobertas, mas percebi que o Firefox não funcionava da mesma maneira. O Firefox tem um comportamento muito estranho com as teclas de seta ao usar SVG e / ou Flexbox. O cursor fica acima da imagem, mas apenas ao pressionar a tecla de seta para a direita.

No entanto, o Firefox funciona bem com um elemento IMG normal.

Para encurtar a história, você terá que renderizar diferentes elementos da imagem com base no navegador.

// chrome contenteditable ads spaces between spans.
// firefox does not, so you have to add them manually.
if (navigator.userAgent.indexOf("Firefox") > 0) {
  Array.from(document.querySelectorAll('#editable span')).forEach(el => {
    el.innerHTML += " "
  })
}
.flexit {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
}

span {
 display: inline-block;
 white-space: pre;
}

.img-wrapper, .img-wrapper + span {
display: inline;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>
  <h1>chrome</h1>
  <div contenteditable="true" class="flexit">
    <span>test</span><svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">       
  <image href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png" height="200" width="200"/>
    </svg><span>Here</span><span>is</span><span>a</span><span>longer</span><span>sentence.</span><span>Notice</span><span>I</span><span>wrapped</span><span>each</span><span>word</span><span>in</span><span>a</span><span>span.</span><span>This</span><span>makes</span><span>the</span><span>text</span><span>appear</span><span>like</span><span>it</span><span>is</span><span>inline.</span>
  </div>

  <h1>firefox</h1>
  <div contenteditable="true" id="editable">
    <span>test</span><span class="img-wrapper"><img src="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png" height="200" width="200" /></span><span>test</span><span>Here</span><span>is</span><span>a</span><span>longer</span><span>sentence.</span><span>Notice</span><span>I</span><span>wrapped</span><span>each</span><span>word</span><span>in</span><span>a</span><span>span.</span><span>This</span><span>makes</span><span>the</span><span>text</span><span>appear</span><span>like</span><span>it</span><span>is</span><span>inline.</span>


  </div>
</body>

</html>

PS Muitos editores que usei padrão para imagens de largura total.

Edit: Acabei de perceber que parece que minha solução Firefox também funciona no Chrome mais recente. Eu acho que isso funciona porque envolvi os nós de texto nos elementos SPAN. O elemento SVG ainda funciona de maneira diferente no Chrome, mas a quebra de texto no SPAN parece resolver a maioria dos problemas.

wizardzeb
fonte
Otimo trabalho!! Com base nisso, tentei colocar o svg em um spancom contenteditable="false"e isso parece funcionar no chrome e no firefox! jsbin.com/xosasuruni/edit?html,output
mowwwalker 25/04
Ah, acho que não display: flexfuncionará, já que agora o texto em cada elemento não faz parte da mesma linha. Se o texto em qualquer um dos elementos for muito longo, não empurrar o outro texto, ele
passará
@mowwwalker você pode fazer com que os elementos sejam agrupados para a próxima linha de usoflex-wrap: wrap;
wizardzeb 25/04
0

Primeiro, não manipule o DOM ProseMirror conforme mostrado no exemplo JQuery. Na verdade, você provavelmente encontrará problemas de DOM ou de conteúdo. O ProseMirror usa seu próprio nó DOM e esquema de marcação. Se você deseja manipular o DOM do ProseMirror ou adicionar plug-in, consulte as Apis de marcação, plug-in e nó. Anexei um exemplo simples de código de marcação de alinhamento de texto. Nota lateral, a razão pela qual a Grammarly e outras pessoas não têm plug-ins ProseMirror é por causa da abordagem / modelos do DOM. Devo acrescentar que o ProseMirror é muito bom, mas, para ser sincero, é mais uma solução de desenvolvedor avançado.

Além disso, as boas notícias são os problemas que você possui em CSS. O ProseMirror retira todas as classes e redefine o DOM / CSS, portanto, se você importar um documento ou recortar e colar todas / quase todas as suas classes desaparecerão.

A abordagem mais simples para resolver isso é agrupar o editor em uma div e atribuir uma classe à div e, em seguida, adicionar estilos e estilos filhos a essa classe. O motivo para empacotá-lo é que os seletores de css, como img, p, h, etc, serão aplicados apenas às tags dentro da classe do editor. Sem isso, você acaba com conflitos óbvios de CSS.

CSS

  1. Não use o flexbox para imagens embutidas, pois o flexbox não é um sistema de grade. De fato, se bem me lembro, você não pode incorporar filhos diretos do contêiner flex.
  2. inline nas tags p e img não quebrará o texto e você terá os problemas listados acima.
  3. se você deseja realmente envolver e remover o problema do cursor, precisará usar flutuadores, por exemplo float: left;(abordagem recomendada)
  4. adicione preenchimento pequeno ou grande e boxe de borda para ajudar a diminuir as bordas e também ajuda na separação de imagem e texto
  5. o problema do cursor que você está enfrentando é porque, quando você dentro do bloco da imagem está alinhado verticalmente ao topo, com o qual você pode corrigir, vertical-align: baseline;mas sem flutuadores, você ainda terá um cursor que corresponde à altura da imagem e não ao texto. Além disso, se você não usar flutuadores, o cursor será alongado, pois a altura da linha é efetivamente a mesma da imagem. A cor azul é apenas seletor, que você altera usando CSS também.
<html>
    <div class="editor">
        <!--        <editor></editor>-->
    </div>
</html>

<style>
    .editor {
        position: relative;
        display: block;
        padding: 10px;
    }

    .editor p {
        font-weight: 400;
        font-size: 1rem;
        font-family: Roboto, Arial, serif;
        color: #777777;
        display: inline;
        vertical-align: baseline;
    }

    .editor img {
        width: 50px;
        float: left;
        padding: 20px;
    }

</style>

Exemplo de extensão de nó

Exemplo de extensão de nó para alinhamento de texto que pode ser adicionado como uma barra de ferramentas. Postagem muito mais longa, mas mesmo que você tenha criado um Nó / plug-in para imagens, terá que lidar com a maneira pela qual ele renderiza, por exemplo, base64 versus url etc., etc. complexidade para desenvolvedores que procuram SEO etc.

export default class Paragraph extends Node {
    get name() {
        return 'paragraph';
    }

    get defaultOptions() {
        return {
            textAlign: ['left', 'center', 'right'],
        }
    }

    inputRules({ type }) {
        return [
            markInputRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)$/, type),
        ]
    }

    get schema() {
        return {
            attrs: {
                textAlign: {
                    default: 'left'
                }
            },
            content: 'inline*',
            group: 'block',
            draggable: false,
            inclusive: false,
            defining : true,
            parseDOM: [
                {
                    tag: 'p',
                    style: 'text-align',
                    getAttrs: value => value
                }
            ],

            toDOM: (node) => [ 'p', {
                style: 'text-align:' + node.attrs.textAlign,
                class: `type--base type--std text-` + node.attrs.textAlign
            }, 0 ]

        };
    }

    commands ({ type }) {
        return (attrs) => updateMark(type, attrs)
    }
}
Nolan
fonte
-2

Eu tentei fazer um pequeno hack com HTML e CSS.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>

<div contenteditable><p class="new1" contenteditable>Hello</p><div contenteditable> 
<img src="https://upload.wikimedia.org/wikipedia/en/9/9b/Yoda_Empire_Strikes_Back.png"></div> 
<p class="new2">Testing</p>
</div>
</body>
</html>

// css

.new2{


font-size:30px;
margin-top:-35px;
margin-left:252px;
padding-left:79px;
}


img{
margin-left:75px;
padding-left:5px;
padding-right:5px;
margin-top:-300px;
}

.new1{
text-overflow:hidden;
padding-top:260px;
margin-bottom:-20px;
font-size:30px;
padding-right:10px;
}

Aqui está o jsfiddle https://jsfiddle.net/wtekxavm/1/

Rishabh dev Tyagi
fonte
Hmm, realmente não resolve o problema, pois apenas codifica o posicionamento dos elementos. Ty para a resposta embora
mowwwalker 25/04
Aceite isso como resposta se você gostou do meu trabalho.
Rishabh dev Tyagi
Essa abordagem é muito rígida para ser prática.
mowwwalker 25/04
Por favor vote em pelo menos
Rishabh dev Tyagi
1
fyi, diminuí a votação porque, dadas as suas respostas, isso não parece uma tentativa de ajudar ou responder à pergunta, mas de conseguir alguns votos positivos.
mowwwalker 27/04