• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Programowanie edytora tekstowego (wysiwyg) HTML JS CSS

Object Storage Arubacloud
+1 głos
493 wizyt
pytanie zadane 30 maja 2022 w HTML i CSS przez okti00 Obywatel (1,300 p.)

Witam, dziś pytanie z innej beczki, ostatnio naszła mnie ochota na ulepszenie edytora tekstu na mojej stronie, stworzyłem coś na wzór popularnych edytorów WYSIWYG, jednakże napotkałem jeden dość niepokojący problem a mianowicie:

  • Do edytora wkleić można dosłownie całą stronę jeśli ją skopiujemy, a jedyne co chciałbym aby dało się wkleić to formatowanie tekstu etc,
  • Wręcz dukam w JS także zapewne kod nie jest optymalny bądź i dla osób znających język totalnie źle napisany, stąd pytanie co mogę zrobić inaczej aby to poprawić / co byście zmienili

Link do edytora: https://codepen.io/AjonEG/pen/VwQQvqN

Z góry dziękuję za pomoc, edytor działa lecz opcja wklejania wszystkiego przeraża..

komentarz 1 czerwca 2022 przez VBService Ekspert (253,340 p.)
edycja 1 czerwca 2022 przez VBService

Przed wysłaniem tekstu, który zawarty jest w edytorze możesz go "wyczyścić" z niepotrzebnych znaków choćby za pomocą String.replace().

komentarz 9 czerwca 2022 przez VBService Ekspert (253,340 p.)

@okti00, Sprawdź to: ALLOYEDITOR

2 odpowiedzi

+2 głosów
odpowiedź 9 czerwca 2022 przez Comandeer Guru (601,110 p.)

Witamy w piekle ^^

Tak się składa, że w pracy zajmuję się rozwojem tego typu edytora, CKEditora (który, swoją drogą, jest wykorzystywany tutaj na forum). I temat filtrowania tekstu przy wklejaniu to jest studnia bez dna. W CKEditorze 4 mamy w tym celu nasz własny parser HTML-a wraz z dość zaawansowanym filtrem, który pozwala odsiać niechciane przez nas rzeczy. Swego czasu napisałem filtr oparty o selektory CSS, zainspirowany właśnie tym w CKE4. Ale obecnie raczej bym szedł w wykorzystanie czegoś typu HTML Sanitizer API z fallbackiem do DomPurify.

Ogólnie problem nie jest prosty, jak zresztą wszystko wokół WYSIWYG-ów. Dlatego IMO lepiej po prostu wziąć gotowca i nie martwić się tego typu problemami.

komentarz 9 czerwca 2022 przez VBService Ekspert (253,340 p.)

Witamy w piekle ^^

yeslaugh

+1 głos
odpowiedź 9 czerwca 2022 przez VBService Ekspert (253,340 p.)
edycja 9 czerwca 2022 przez VBService

Możesz spróbować "obsłużyć" zdarzenie paste i użyć np.

  const text = e.clipboardData.getData('text/plain');
  document.execCommand("insertHTML", false, text);

 

przykład

testowy kod do wklejenia (sprawdź po wklejeniu - devtool)

<!DOCTYPE>
<html lang="pl-PL">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

  </head>
  <body>
    <p>Lorem ipsum dolor sit amet, <b>consectetur</b> adipiscing elit</p>
    <ul>
      <li>Lorem ipsum</li>
      <li>Lorem ipsum dolor</li>
    </ul>
    <br>
    <blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">
      <u>Lorem ipsum</u>
    </blockquote>
    <p>
      <a href="https://forum.pasja-informatyki.pl/567301/programowanie-edytora-tekstowego-wysiwyg-html-js-css?show=567791#a567791" target="_blank">Link</a>
    </p>
    <div>
      <img src="https://picsum.photos/300/200/?random=1" alt="Losowy obrazek">
    </div>
  </body>
</html>

 

propozycja zmian

<div id="editor-ajoneg">
  <div class="toolbar">
    <img class="toolbar--button primary" src="https://i.imgur.com/o6shbuy.png" data-command="bold">
    <img class="toolbar--button primary" src="https://i.imgur.com/mNEdciT.png" data-command="italic">
    <img class="toolbar--button primary" src="https://i.imgur.com/IXbEqti.png" data-command="underline">
    <img class="toolbar--button primary" src="https://i.imgur.com/r4z50sm.png" data-command="strikeThrough">
    <img class="toolbar--button primary" src="https://i.imgur.com/Hb3b8An.png" data-command="changeVisiblityMenu">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/GTJDSHp.png" data-command="removeFormat">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/vDTM9qQ.png" data-command="justifyFull">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/XM8uYXK.png" data-command="justifyLeft">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/kDA7qt0.png" data-command="justifyCenter">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/LFHApGm.png" data-command="justifyRight">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/S8cbkqa.png" data-command="indent">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/fpDXRvz.png" data-command="outdent">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/U2npFKQ.png" data-command="addUrl">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/MGnTXZ8.png" data-command="unlink">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/CqHXOJc.png" data-command="insertUnorderedList">
    <img class="toolbar--button secondary hide" src="https://i.imgur.com/NThoJn6.png" data-command="insertOrderedList">

    <!-- Nie miałem innych ikon ;-)   BTW, w css to: .toolbar--button:nth-last-of-type(2) -->
    <img class="toolbar--button primary" src="https://i.imgur.com/Hb3b8An.png" data-command="cleanEditArea">
    <img class="toolbar--button primary" src="https://i.imgur.com/Hb3b8An.png" data-command="changeEditMode">
  </div>
  <div class="toolbar--addurl">
    <input type="url" pattern="https://.*" placeholder="Podaj adres np.: https://google.pl/">
    <button data-command="createLink" disabled>Wstaw link</button>
  </div>
  <div contenteditable="true" data-contenteditable="true" id="textarea" data-placeholder="Wpisz treść ..."></div>
</div>
#editor-ajoneg {
  max-width: 720px;
  min-width: 720px;
}
#editor-ajoneg .toolbar {
  display: flex;
  justify-content: flex-start;
  padding: 0.25em;
  border: 1px solid black;
  margin-bottom: 0.25em;
  background-color: rgba(0,0,0,0.9);
}
#editor-ajoneg .toolbar .toolbar--button {
  border: 0;
  margin: 0 0.25em;
  width: 1.6em;
  height: 1.6em;
  cursor: pointer;
  border-radius: 0.25em;
  transition: background 0.5s, transform 0.5s;
}
#editor-ajoneg .toolbar .toolbar--button:nth-last-of-type(2) { /* last-of-type */
  margin-left: auto;
}
#editor-ajoneg .toolbar .secondary {
  transition: opacity 1s, background 0.5s, transform 0.5s;
}
#editor-ajoneg .toolbar .toolbar--button:hover {
  background-color: rgba(255,55,255,0.2);
  transform: scale(1.15);
}
#editor-ajoneg #textarea {
  max-height: 200px;
  height: 200px;
  padding: 0.5em;
  border: 1px solid rgba(0,0,0,0.3);
  overflow-x: hidden;
}
#editor-ajoneg #textarea::-webkit-scrollbar {
  width: 0.5em;
}
#editor-ajoneg #textarea::-webkit-scrollbar-track {
  border: 0.2em solid transparent;
  background-color: rgba(255,55,255,0.2);
}
#editor-ajoneg #textarea::-webkit-scrollbar-thumb {
  width: 0.4em;
  background-color: rgba(0,0,0,0.9);
  border-radius: 0.2em;
}
#editor-ajoneg #textarea[contentEditable=true]:empty:not(:focus)::before {
  content: attr(data-placeholder);
  font: 300 1em/1.1 system-ui;
}
#editor-ajoneg #textarea[data-contenteditable="false"] {
  border: 0 !important;
}
#editor-ajoneg .toolbar--addurl {
  display: none;
  margin: 0.5em 0;
}
#editor-ajoneg .toolbar--addurl input {
  width: 400px;
}
#editor-ajoneg .toolbar--addurl button {
  cursor: default-button;
}
#editor-ajoneg .toolbar--addurl button:disabled {
  cursor: default;
}

#editor-ajoneg .show {
  display: block;
}
#editor-ajoneg .hide {
  opacity: 0;
}
let editor, editor_toolbar, editor_textarea,
    editor_url_input, editor_url_button;
window.onload = initializeEditor;

function initializeEditor() {
  editor = document.querySelector('#editor-ajoneg');

  editor_toolbar = editor.querySelector('.toolbar');
  editor_toolbar.onclick = toolbarCommand;  

  editor_textarea = editor.querySelector('#textarea');
  editor_textarea.onpaste = cleanDataFromClipboard;

  editor_url_input = editor.querySelector('.toolbar--addurl input');  
  editor_url_input.oninput = checkUrl;
  editor_url_button = editor.querySelector('.toolbar--addurl button');
  editor_url_button.onclick = toolbarCommand;
}

function toolbarCommand(e) {
  if (e.target.nodeName == 'IMG' && e.target.classList.contains('toolbar--button')) {
    switch (e.target.dataset.command) {
      case 'changeVisiblityMenu': changeVisiblityMenu(); break;
      case 'addUrl': addUrlShow(); break;
      case 'cleanEditArea': cleanEditArea(); break;
      case 'changeEditMode': changeEditMode(); break;
      default:
        document.execCommand(e.target.dataset.command, false, null); 
        editor_textarea.focus();
        break;
    }
  }

  if (e.target.nodeName == 'BUTTON') {
    switch (e.target.dataset.command) {
      case 'createLink': 
        document.execCommand('createLink', false, editor_url_input.value.trim());
        editor_textarea.focus();
        addUrlHide();
        break;
    }
  }
}

function changeVisiblityMenu() {
  const buttons = [...document.querySelectorAll('.toolbar .secondary')];
  if (buttons[0].classList.contains('hide')) 
    buttons.map((i) => (i.classList.remove('hide')));
  else
    buttons.map((i) => (i.classList.add('hide')));
}

function addUrlShow() {
  document.querySelector('.toolbar--addurl').classList.add('show');
  editor_url_input.focus();
} 
function addUrlHide() {
  document.querySelector('.toolbar--addurl').classList.remove('show');
  editor_url_input.value = null;
  editor_url_button.disabled = true;
  editor_textarea.focus();
}

function checkUrl(e) {
  const value = e.target.value.trim();
  editor_url_button.disabled = (value.match(e.target.pattern))? false : true;
}

function changeEditMode() {
  if (editor_textarea.dataset.contenteditable == 'true') {
    editor_textarea.setAttribute('contenteditable', false);
    editor_textarea.dataset.contenteditable = false;
  } else {
    editor_textarea.setAttribute('contenteditable', true);
    editor_textarea.dataset.contenteditable = true;
  }  
}

function cleanEditArea() {
  editor_textarea.innerHTML = '';
}

function cleanDataFromClipboard(e) { // onpaste
  e.preventDefault();
  const text = e.clipboardData.getData('text/plain');
  document.execCommand("insertHTML", false, text);
}

 

Podobne pytania

0 głosów
4 odpowiedzi 220 wizyt
pytanie zadane 20 maja 2016 w C i C++ przez Koko$ Użytkownik (740 p.)
+2 głosów
1 odpowiedź 277 wizyt
pytanie zadane 5 października 2022 w JavaScript przez Wally Bywalec (2,840 p.)
+1 głos
1 odpowiedź 276 wizyt

92,572 zapytań

141,422 odpowiedzi

319,643 komentarzy

61,959 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...