Zapobiegaj dodawaniu edytowalnej zawartości <div> w ENTER - Chrome

140

Mam contenteditableelement i za każdym razem, gdy piszę coś i naciskam ENTER, tworzy nowy <div>i umieszcza tam nowy tekst. Ani trochę mi się to nie podoba.

Czy można temu zapobiec lub przynajmniej po prostu zastąpić to znakiem <br>?

Oto demo http://jsfiddle.net/jDvau/

Uwaga: to nie jest problem w przeglądarce Firefox.

iConnor
źródło
1
firefox dodaje <br>, chrome - nie, ale po poprawieniu stylów dodatkowe elementy div nie przerywają lewego dopełnienia. Pytanie brzmi, dlaczego ci się to nie podoba? Pomyśl, że to br ... jsfiddle.net/jDvau/1 Możesz również użyć zdarzenia DOMSubtreeModified do złapania tych elementów div i ich usunięcia.
ViliusL,
stackoverflow.com/questions/6024594/ ... to może ci pomóc, powodzenia!
Sirikon,
1
Dla mnie rozwiązanie Blake Plumb jest tutaj najprostsze i zdecydowanie najlepsze.
svassr
1
@svassr nie o to chodzi, to nie ty czy ja będziemy go używać. To klient, który może nawet nie wiedzieć, co to jest zmiana.
iConnor,
2
Rzeczywiście, zmienia wszystko. To powiedziawszy, jest to typowe zachowanie, a mała wiadomość z pomocą nie byłaby uzbrojona. „Daj człowiekowi rybę, a nakarmisz go na jeden dzień. Naucz człowieka łowić ryby, a nakarmisz go na całe życie”.
svassr

Odpowiedzi:

164

Spróbuj tego:

$('div[contenteditable]').keydown(function(e) {
    // trap the return key being pressed
    if (e.keyCode === 13) {
        // insert 2 br tags (if only one br tag is inserted the cursor won't go to the next line)
        document.execCommand('insertHTML', false, '<br/>');
        // prevent the default behaviour of return key pressed
        return false;
    }
});

Kliknij tutaj, aby zobaczyć demo

Ram G Athreya
źródło
Ale tutaj widzę małą różnicę. Umieść kursor w pustym wierszu (na górze „Wpisz coś”) i naciśnij Enter. Kursor znajduje się teraz tuż przed „Typem”, a nie w nowej pustej linii.
Andrew,
3
To nie działa w IE11, ponieważ nie obsługuje insertHTML. Zobacz odpowiedź poniżej!
webprogrammer
4
Jeśli umieścisz kursor między znakami np. „Ty [kursor] pe some stuff” i naciśnij Enter, a otrzymasz o 1 linię za dużo.
Chandrew
13
Odpowiedź jest nie do przyjęcia. Rozwiązaniem byłoby posiadanie tylko jednego <br />
raoulińskiego
3
ten powrót zarówno <br>, jak i <div>
Nishad Up
51

Możesz to zrobić za pomocą samej zmiany CSS:

div{
    background: skyblue;
    padding:10px;
    display: inline-block;
}

pre{
    white-space: pre-wrap;
    background: #EEE;
}

http://jsfiddle.net/ayiem999/HW43Q/

airi
źródło
1
Wystąpił problem, gdy użyłem inline-block, wykonałem execCommand („insertHTML”, xxx), aby coś wstawić, na końcu wstawianego elementu zostanie dodany znak „<br>”, przetestowany w Chrome33.
Imskull
5
Niezłe znalezisko. Niestety nie zadziała to na pozycjach: absolutnych elementach lub elementach, które są bezpośrednimi dziećmi rodzica z display: flex. Możesz go zhakować do pracy, pamiętając o tym. Tylko upewnij się, że nie jest to bezpośrednie dziecko elementów elastycznych. Jeśli potrzebujesz pozycji: absolutna, po prostu daj jej dodatkowego rodzica z tym.
Sceptic
@ReinoutvanKempen Zaczął używać flex już 3 miesiące temu. Myślę, że ten hack nie zadziała
kittu
to naprawdę najlepsze rozwiązanie, jest ekstremalnie czyste i przejrzyste, nic nie zostanie wstawione, nawet zawiera br. a zwłaszcza to rozwiązanie bez js.
bronić orki
2
Wow ... to rozwiązuje Chrome, ale sprawia, że ​​Firefox zaczyna dodawać elementy div przy wejściu, czego domyślnie nie robi.
cbdeveloper
42

Dodaj styl display:inline-block;do contenteditable, nie wygeneruje div, pa spanautomatycznie w Chrome.

Le Tung Anh
źródło
7
To świetne rozwiązanie. *[contenteditable="true"]{display: inline-block;}
ericjbasti
Uwielbiam to, proste, ale skuteczne
user25794
W IE11 będzie to dodatkowy <p></p> :(
Betty St
1
ale stworzy kolejny bardzo irytujący błąd Chrome (co za gówniana przeglądarka)
vsync
5
Nie działa już z Chrome w wersji 63.0.3239.84
Mikaël Mayer
22

Spróbuj tego:

$('div[contenteditable="true"]').keypress(function(event) {

    if (event.which != 13)
        return true;

    var docFragment = document.createDocumentFragment();

    //add a new line
    var newEle = document.createTextNode('\n');
    docFragment.appendChild(newEle);

    //add the br, or p, or something else
    newEle = document.createElement('br');
    docFragment.appendChild(newEle);

    //make the br replace selection
    var range = window.getSelection().getRangeAt(0);
    range.deleteContents();
    range.insertNode(docFragment);

    //create a new range
    range = document.createRange();
    range.setStartAfter(newEle);
    range.collapse(true);

    //make the cursor there
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);

    return false;
});

http://jsfiddle.net/rooseve/jDvau/3/

Andrzej
źródło
Dobrze, działa :) Czekam na więcej uwagi zanim zdecyduję, dzięki.
iConnor
nie działa poprawnie w przeglądarce Firefox. dodaje jedną dodatkową linię.
agpt
To sprawia, że inputnie jest uruchamiany po naciśnięciu klawisza Enter. Proste rozwiązanie: dodaj coś takiego jak$(this).trigger('input')
LarsW
insertHTMLRozwiązanie robi dziwne rzeczy, kiedy nie są zagnieżdżone contenteditableelementy, twoje omija rozwiązanie, ale istnieje kilka problemów, dodałem go jako nowego możliwego rozwiązania.
skerit
Nie działa w Chrome. Pierwsze naciśnięcie klawisza Enter na końcu ciągu dodaje spację. Jednak za drugim razem to działa.
20
document.execCommand('defaultParagraphSeparator', false, 'p');

Zastępuje domyślne zachowanie, aby zamiast tego mieć akapit.

W Chrome domyślne zachowanie przy wejściu to:

<div>
    <br>
</div>

Z tym poleceniem tak będzie

<p>
    <br>
</p>

Teraz, gdy jest bardziej liniowy w poprzek, łatwo jest go mieć tylko <br>wtedy, gdy potrzebujesz.

Ced
źródło
Kiedy naciśniesz Enter, zamiast div i br będziesz mieć p i br w przeglądarce. Spróbuj.
Ced
Dzięki! Wyjaśnienia są zawsze pomocne
jpaugh
@jpaugh np, zredagowałem swoją odpowiedź dalej, aby lepiej wyjaśniała.
Ced
to nie działa na Firefoksie (testowałem tylko w Chrome i Firefox)
medBouzid
FireFox został naprawiony tak, aby teraz respektował defaultParagraphSeparator. IMHO to sprawia, że ​​ta odpowiedź jest najlepsza, ponieważ sprawia, że ​​wszystkie przeglądarki są teraz spójne i zgodne ze specyfikacją. Jeśli następnie nie chcesz, aby znacznik P miał marginesy „odstępów” od poprzedniego bloku tekstu w edytowalnej treści, możesz użyć CSS, aby to zmienić.
blackmamba
9

Użyj shift+ enterzamiast enterpo prostu wstawić pojedynczy <br>tag lub zawinąć tekst w <p>tagi.

Blake Plumb
źródło
9
+1 Dzięki, jest to bardzo przydatne, ale jeśli masz klienta, który pisze książkę, będzie to uciążliwe ***
iConnor
1
To nie pomaga, jeśli
wklejasz
8

Działa to we wszystkich głównych przeglądarkach (Chrome, Firefox, Safari, Edge)

document.addEventListener('keydown', event => {
  if (event.key === 'Enter') {
    document.execCommand('insertLineBreak')
    event.preventDefault()
  }
})
<div class="element" contenteditable="true">Sample text</div>
<p class="element" contenteditable="true">Sample text</p>

Jest jedna niedogodność. Po zakończeniu edycji elementy mogą zawierać zakończenie w <br>środku. Ale możesz dodać kod, aby go zmniejszyć, jeśli zajdzie taka potrzeba.

Zaznacz tę odpowiedź, aby usunąć końcowe <br> https://stackoverflow.com/a/61237737/670839


źródło
1
Co za cholera? W jaki sposób jedyne rozwiązanie, które faktycznie zadziałało dla mnie, może znajdować się tak daleko na stronie? Dzięki wielkie.
user12861
Właśnie sprawiłeś, że mój dzień zajęło mi wiele godzin - dzięki!
Alexander Kludt
5

Sposób, w jaki contenteditablezachowuje się po naciśnięciu, enterzależy od przeglądarek, rozszerzenie<div> dzieje się tak w przypadku Webkita (Chrome, Safari) i IE.

Zmagałem się z tym kilka miesięcy temu i poprawiłem to w ten sposób:

//I recommand you trigger this in case of focus on your contenteditable
if( navigator.userAgent.indexOf("msie") > 0 || navigator.userAgent.indexOf("webkit") > 0 ) {
    //Add <br> to the end of the field for chrome and safari to allow further insertion
    if(navigator.userAgent.indexOf("webkit") > 0)
    {
        if ( !this.lastChild || this.lastChild.nodeName.toLowerCase() != "br" ) {
            $(this).html( $(this).html()+'<br />' );
        }
    }

    $(this).keypress( function(e) {
        if( ( e.keyCode || e.witch ) == 13 ) {
            e.preventDefault();

            if( navigator.userAgent.indexOf("msie") > 0 ) {
                insertHtml('<br />');
            }
            else {
              var selection = window.getSelection(),
              range = selection.getRangeAt(0),
              br = document.createElement('br');

              range.deleteContents();
              range.insertNode(br);
              range.setStartAfter(br);
              range.setEndAfter(br);
              range.collapse(false);

              selection.removeAllRanges();
              selection.addRange(range);
            }
        }
    });
}

Mam nadzieję, że to pomoże i przepraszam za mój angielski, jeśli nie jest tak jasny, jak potrzeba.

EDYCJA : Korekta usuniętej funkcji jQueryjQuery.browser

Elie
źródło
Chcemy tylko poinformować, że jQuery.browsernie jest już częścią jQuery.
iConnor
Masz rację, powinienem o tym wspomnieć i użyć czegoś takiego jak navigator.userAgent.indexOf("msie") > 0inavigator.userAgent.indexOf("webkit") > 0
Elie,
1
if( ( e.keyCode || e.witch ) == 13 ) { ... }musi byćif (e.keyCode === 13 || e.which === 13) { ... }
Sebastian Sandqvist
5

Możesz mieć osobne <p>tagi dla każdego wiersza, zamiast używać <br>tagów i uzyskać większą zgodność przeglądarki po wyjęciu z pudełka.

Aby to zrobić, umieść <p>tag z domyślnym tekstem wewnątrz edytowalnego elementu div.

Na przykład zamiast:

<div contenteditable></div>

Posługiwać się:

<div contenteditable>
   <p>Replace this text with something awesome!</p>
</div>

jsfiddle

Testowany w Chrome, Firefox i Edge, a drugi działa tak samo w każdym.

Jednak pierwszy tworzy elementy div w Chrome, tworzy podziały wierszy w Firefoksie, aw Edge tworzy elementy div, a kursor jest umieszczany z powrotem na początku bieżącego elementu div, zamiast przesuwać się do następnego.

Przetestowano w Chrome, Firefox i Edge.

Josh Powlison
źródło
to właściwie nie jest złe rozwiązanie
AaronHS
5

Lubię używać pułapki na myszy do obsługi skrótów klawiszowych: https://craig.is/killing/mice

Następnie po prostu przechwytuję zdarzenie enter, wykonując polecenie insertLineBreak :

Mousetrap.bindGlobal('enter', (e)=>{
  window.document.execCommand('insertLineBreak', false, null);
  e.preventDefault();
});

Wszystkie polecenia: https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand

Działa przy użyciu przeglądarki Chrome 75 i następującego edytowalnego elementu:

<pre contenteditable="true"></pre>

Możliwe jest również użycie insertHTML :

window.document.execCommand('insertHTML', false, "\n");
Gaus
źródło
Nic nie robi po mojej stronie…
Matt
4

dodaj opcję zapobiegania domyślnego do elementu div

document.body.div.onkeydown = function(e) {
    if ( e.keycode == 13 ){
        e.preventDefault();
            //add a <br>
        div = document.getElementById("myDiv");
        div.innerHTML += "<br>";
    }
}
Chiller Math
źródło
@connorspiracist sorry to document.body.div(prawdopodobnie będziesz musiał wstawić inny kod, aby wskazać, aby poprawić divjego ogólny pomysł)
Math chiller
4

Użyłbym stylizacji (CSS), aby rozwiązać problem.

div[contenteditable=true] > div {
  padding: 0;
} 

Firefox rzeczywiście dodaje przerwanie elementu blokowego
, podczas gdy Chrome otacza każdą sekcję tagiem. Twój css daje elementom div wypełnienie 10 pikseli wraz z kolorem tła.

div{
  background: skyblue;
  padding:10px;
}

Alternatywnie możesz powtórzyć ten sam pożądany efekt w jQuery:

var style = $('<style>p[contenteditable=true] > div { padding: 0;}</style>');
$('html > head').append(style);

Oto widelec twoich skrzypiec http://jsfiddle.net/R4Jdz/7/

cbayram
źródło
1
Może to pomóc niektórym osobom, ale bardziej martwiłem się niepotrzebnymi znacznikami, ponieważ brałbym ostateczny kod HTML contenteditablei używał go w innym miejscu
iConnor
3

Możesz otoczyć akapity <p>tagiem, na przykład pojawi się w nowej linii zamiast div Przykład:
<div contenteditable="true"><p>Line</p></div>
Po wstawieniu nowego ciągu:
<div contenteditable="true"><p>Line</p><p>New Line</p></div>

nes
źródło
3

Rozwiązanie inserHTMLpoleceń robi dziwne rzeczy po zagnieżdżeniucontenteditable elementy.

Wziąłem kilka pomysłów z wielu odpowiedzi tutaj i wydaje się, że na razie odpowiada to moim potrzebom:

element.addEventListener('keydown', function onKeyDown(e) {

    // Only listen for plain returns, without any modifier keys
    if (e.which != 13 || e.shiftKey || e.ctrlKey || e.altKey) {
        return;
    }

    let doc_fragment = document.createDocumentFragment();

    // Create a new break element
    let new_ele = document.createElement('br');
    doc_fragment.appendChild(new_ele);

    // Get the current selection, and make sure the content is removed (if any)
    let range = window.getSelection().getRangeAt(0);
    range.deleteContents();

    // See if the selection container has any next siblings
    // If not: add another break, otherwise the cursor won't move
    if (!hasNextSibling(range.endContainer)) {
        let extra_break = document.createElement('br');
        doc_fragment.appendChild(extra_break);
    }

    range.insertNode(doc_fragment);

    //create a new range
    range = document.createRange();
    range.setStartAfter(new_ele);
    range.collapse(true);

    //make the cursor there
    let sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);

    e.stopPropagation();
    e.preventDefault();

    return false;
});

// See if the given node has a next sibling.
// Either any element or a non-empty node
function hasNextSibling(node) {

    if (node.nextElementSibling) {
        return true;
    }

    while (node.nextSibling) {
        node = node.nextSibling;

        if (node.length > 0) {
            return true;
        }
    }

    return false;
}
skerit
źródło
2

Najpierw musimy przechwycić każdy klawisz wprowadzany przez użytkownika, aby sprawdzić, czy naciśnięto klawisz Enter, a następnie zapobiegamy <div>tworzeniu i tworzymy własny <br>tag.

Jest jeden problem, kiedy tworzymy go, nasz kursor pozostaje w tej samej pozycji, więc używamy Selection API, aby umieścić kursor na końcu.

Nie zapomnij dodać <br>tagu na końcu tekstu, ponieważ jeśli tego nie zrobisz, pierwsza enter nie utworzy nowej linii.

$('div[contenteditable]').on('keydown', function(e) {
    var key = e.keyCode,
        el  = $(this)[0];
    // If Enter    
    if (key === 13) {
        e.preventDefault(); // Prevent the <div /> creation.
        $(this).append('<br>'); // Add the <br at the end

        // Place selection at the end 
        // http://stackoverflow.com/questions/4233265/contenteditable-set-caret-at-the-end-of-the-text-cross-browser
        if (typeof window.getSelection != "undefined"
            && typeof document.createRange != "undefined") {
            var range = document.createRange();
            range.selectNodeContents(el);
            range.collapse(false);
            var sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (typeof document.body.createTextRange != "undefined") {
            var textRange = document.body.createTextRange();
            textRange.moveToElementText(el);
            textRange.collapse(false);
            textRange.select();
        }
    }
});

Skrzypce

L105
źródło
2

To jest edytor HTML5 obsługiwany przez przeglądarkę. Możesz zawijać swój tekst <p>...</p>, a następnie za każdym razem, gdy naciśniesz ENTER, otrzymasz <p></p>. Edytor działa również w ten sposób, że każde naciśnięcie SHIFT + ENTER wstawia <br />.

<div contenteditable="true"><p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolor veniam asperiores laudantium repudiandae doloremque sed perferendis obcaecati delectus autem perspiciatis aut excepturi et nesciunt error ad incidunt impedit quia dolores rerum animi provident dolore corporis libero sunt enim. Ad magnam omnis quidem qui voluptas ut minima similique obcaecati doloremque atque!
<br /><br />
Type some stuff, hit ENTER a few times, then press the button.
</p>
</div>

Sprawdź to: http://jsfiddle.net/ZQztJ/

Mehdi
źródło
1
if (navigator.userAgent.toLowerCase().indexOf('msie') > -1) {
   var range = document.getSelection();
   range.pasteHTML(range.htmlText + '<br><br>');
}
else if(navigator.userAgent.toLocaleLowerCase().indexOf('trident') > -1)                           {
   var range = document.getSelection().getRangeAt(0); //get caret
   var nnode = document.createElement('br');
   var bnode = document.createTextNode('\u00A0'); //&nbsp;
   range.insertNode(nnode);
   this.appendChild(bnode);
   range.insertNode(nnode);                                
}
else
   document.execCommand('insertHTML', false, '<br><br>')

Gdzie thisjest rzeczywisty kontekst, który oznacza document.getElementById('test');.

programista internetowy
źródło
0

Inny sposób na zrobienie tego

$('button').click(function(){
    $('pre').text($('div')[0].outerHTML)
});

$("#content-edit").keydown(function(e) {
    if(e.which == 13) {
       $(this).find("div").prepend('<br />').contents().unwrap();
      }
});

http://jsfiddle.net/jDvau/11/

MrAJ
źródło
Nie pozwala na przesuwanie kursora do przodu podczas pisania.
Jack Lu
To dziwne, właśnie próbowałem w Chrome i IE 11 i działa dobrze. Odniesienie Czy mógłbyś mi powiedzieć, której przeglądarki używasz? Dalsze szczegóły dotyczące zachowania byłyby pomocne w rozwiązaniu problemu
MrAJ
0

Zapobiegaj tworzeniu nowych elementów Div w edytowalnych treściach Div przy każdym klawiszu Enter: Rozwiązanie, które znalazłem, jest bardzo proste:

var newdiv = document.createElement("div"); 
newdiv.innerHTML = "Your content of div goes here";
myEditablediv.appendChild(newdiv);

Ta zawartość --- innerHTML zapobiega tworzeniu nowych elementów Div w edytowalnych treściach Div przy każdym klawiszu Enter.

Dr Shyam Babu Gupta
źródło
0

W wersji roboczej edytora W3C jest kilka informacji o dodawaniu stanów do ContentEditable i zapobieganiu dodawaniu przez przeglądarkę nowego elementu po naciśnięciu klawisza Enter plaintext-only.

<div contentEditable="plaintext-only"></div>
StefansArya
źródło
-1

Można całkowicie zapobiec temu zachowaniu.

Zobacz to skrzypce

$('<div class="temp-contenteditable-el" contenteditable="true"></div>').appendTo('body').focus().remove();
Omnicon
źródło