Posición del caret y altura de selección para elementos en línea en Chrome


8

Estoy buscando construir un editor con Slate o ProseMirror y veo un comportamiento extraño con Chrome alrededor de la posición de intercalación y el área de selección cuando utilizo un elemento en línea. El problema 1 se muestra en la primera imagen a continuación. Cuando la posición del cursor de texto está detrás de la "f", el cursor se muestra en la parte superior de la imagen. El problema 2 está en la segunda imagen: al seleccionar el texto, se muestra un área resaltada que es tan alta como en el elemento en línea. ¿Hay alguna manera de controlar este comportamiento y, en cambio, haga que el cursor se muestre en la posición del texto y solo resalte el espacio alrededor del texto (incluso si la imagen en línea está haciendo que la altura de la línea sea más grande) posición de cuidado demasiado alta área de selección demasiado grande

Me gustaría imitar el comportamiento aquí, desde firefox: ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Se produjeron imágenes de ejemplo usando la demostración ProseMirror aquí: https://prosemirror.net/examples/basic/

Un ejemplo mínimo (gracias @Kaiido) con JSBin :

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

No estoy seguro de cómo se comporta esto en otros sistemas operativos, pero estoy usando macOS Catalina.


2
... Podría hacer un ejemplo reproducible mínimo con solo <div contenteditable>Test text<img src="https://upload.wikimedia.org/wikipedia/en/9/9b/Yoda_Empire_Strikes_Back.png">Testing</div>. Ahora, así es como lo hacen, actualmente no hay especificaciones en torno a eso, por lo que son bastante libres de hacer lo que consideran mejor. ¿Necesita absolutamente estas bibliotecas, o una solución en bruto es suficiente para usted?
Kaiido

1
Solo un comentario: TinyMCE parece comportarse bastante similar en Chrome, excepto que estira el cursor hasta la altura de la imagen.
V.Volkov

gracias @Kaiido, incluirá en la pregunta.
mowwwalker

Creo que está relacionado con el sistema operativo. Veo el mismo comportamiento @ V.Volkov que acabo de describir en Chrome en Mac.
Dekel

Respuestas:


4

Puede solucionar el problema de selección utilizando flexbox. Originalmente intenté usar align-items: flex-endpero estaba obteniendo un comportamiento extraño de tecla de flecha. align-items: baselineparece funcionar a partir de ahora.

El problema de la imagen es muy extraño. Noté que Chrome pasa sobre los elementos SVG de manera diferente a los elementos IMG. A partir de ahora, la última versión de Chrome "espera" antes de omitir IMG, pero permite al usuario omitir un SVG como cualquier otro carácter (la flecha izquierda omite el carácter más cercano a svg). Esto puede ser causado por los estilos predeterminados subyacentes, pero no lo sé.

Estaba a punto de publicar mis hallazgos, pero me di cuenta de que Firefox no funcionaba igual. Firefox tiene un comportamiento de tecla de flecha realmente extraño cuando se usa SVG y / o Flexbox. El cursor pasa sobre la imagen, pero solo cuando se presiona la tecla de flecha derecha.

Sin embargo, Firefox funciona bien con un elemento IMG normal.

En pocas palabras, tendrá que representar diferentes elementos de imagen en función del 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>

PD: muchos editores que he usado por defecto para imágenes de ancho completo.

Editar: Acabo de darme cuenta de que parece que mi solución de Firefox también funciona en la última versión de Chrome. Creo que esto funciona porque envolví los nodos de texto en elementos SPAN. El elemento SVG todavía funciona de manera diferente en Chrome, pero ajustar el texto en SPAN parece resolver la mayoría de los problemas.


¡¡Impresionante trabajo!! Partiendo de eso, intenté poner el svg en un spancon contenteditable="false"y eso parece funcionar en Chrome y Firefox. jsbin.com/xosasuruni/edit?html,output
mowwwalker

Ah, no creo display: flexque funcione ya que ahora el texto en cada elemento no es parte de la misma línea. Si el texto en cualquiera de los elementos es demasiado largo, no empuja el otro texto, pasa a la siguiente línea
mowwwalker

@mowwwalker puedes hacer que los elementos se ajusten a la siguiente líneaflex-wrap: wrap;
wizardzeb

0

Primero, no manipule el DOM de ProseMirror como se muestra en el ejemplo de JQuery. De hecho, lo más probable es que te encuentres con DOM o problemas de contenido. ProseMirror usa su propio nodo DOM y esquema de marcado. Si desea manipular el DOM de ProseMirror o agregar un complemento, eche un vistazo a las Apis de marcado, complemento y nodo. He adjuntado un ejemplo simple de texto alinear código de marcado. Nota al margen, la razón por la que Grammarly y otros no tienen complementos de ProseMirror se debe al enfoque / modelos DOM. Debo agregar que ProseMirror es muy bueno, pero para ser honesto, es más una solución de desarrollador avanzada.

Aparte de eso, la buena noticia son los problemas que tiene un CSS puro. ProseMirror elimina todas las clases y restablece el DOM / CSS, por lo que si importa un documento o corta y pega todas / la mayoría de sus clases desaparecerán.

El enfoque más simple para resolver es envolver el editor en un div y asignar una clase al div y luego agregar estilos y estilos secundarios a esa clase. La razón para envolverlo es que los selectores css como img, p, h, etc. solo se aplicarán a las etiquetas dentro de la clase del editor. Sin eso, terminas con obvios enfrentamientos CSS.

CSS

  1. No use flexbox para imágenes en línea ya que flexbox no es un sistema de cuadrícula. De hecho, si recuerdo no se pueden alinear hijos directos del contenedor flexible.
  2. en línea en las etiquetas p e img no ajustará el texto y terminará con los problemas enumerados anteriormente.
  3. si realmente quiere envolver y eliminar el problema del cursor que tiene, entonces necesita usar flotadores, por ejemplo float: left;(enfoque recomendado)
  4. agregue relleno pequeño o grande y recuadro de borde para ayudar a contraer los bordes y también ayuda con la separación de imagen y texto
  5. el problema con el cursor que está experimentando es porque cuando dentro de la imagen la bloquea alineada verticalmente con la parte superior, lo que puede solucionar vertical-align: baseline;pero sin flotadores todavía tendrá un cursor que coincida con la altura de la imagen y no con el texto. Además, si no utiliza flotantes, el cursor se alargará ya que la altura de la línea es efectivamente la misma altura que la imagen. El color azul es solo un selector, que también puedes cambiar usando CSS.
<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>

Ejemplo de extensión de nodo

Ejemplo de extensión de nodo para alinear texto que se puede agregar como barra de herramientas. Publicación mucho más larga, pero incluso si creó un Nodo / complemento para imágenes, tiene que lidiar con la forma en que se procesa, es decir, base64 versus url, etc., por cierto, tiene mucho sentido por qué lo hicieron, pero solo agregue complejidad para desarrolladores que buscan 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)
    }
}

-2

Traté de hacer un pequeño truco con HTML y 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;
}

Aquí está jsfiddle https://jsfiddle.net/wtekxavm/1/


Hmm, realmente no resuelve el problema, ya que solo codifica el posicionamiento de los elementos. Ty por la respuesta sin embargo
mowwwalker

Acepta esto como respuesta si te gustó mi trabajo.
Rishabh dev Tyagi

Este es un enfoque demasiado rígido para ser práctico.
mowwwalker

Por favor vote al menos
Rishabh dev Tyagi

1
Para su información, voté en contra porque, dadas sus respuestas, esto no parece un intento de ayudar o responder a la pregunta, sino de enganchar algunos votos positivos.
mowwwalker
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.