Bruce Gust
asked on
Why does my <pre> tag get moved by TinyMCE?
I'm using TinyMCE as a text editor and it's working great except for one very weird anomaly.
This is what I'm inserting:
<div class="code_frame">
<div class="inner_code_frame" style="margin-top: 5px;">
<div class="paper">
<pre style="font-family: arial; font-size: 9pt;">
<html>
<table>
<tr>
<td>
hello
</td>
</tr>
</table>
</html>
</pre>
</div>
</div>
</div>
This is what gets inserted...
<div class="code_frame">
<div class="inner_code_frame" style="margin-top: 5px;">
<div class="paper">
<table>
<tbody>
<tr>
<td>hello</td>
</tr>
</tbody>
</table>
<pre style="font-family: arial; font-size: 9pt;"> </pre>
</div>
</div>
</div>
Notice how <pre style="font-family: arial; font-size: 9pt;"> </pre> has gotten completely displaced?
Why?
And how do I fix that?
ASKER
Leak!
I was able to do a little more research since posting the question and here's what I found...
This gets inserted into the database correctly:
It's not that the content as it was entered into the database is wrong. That piece is fine.
But when I go to edit it and ask TinyMCE to reproduce what's in the database, it's then that the <pre> tag gets moved.
I'm looking at this: https://stackoverflow.com/questions/56407393/why-does-tinymce-move-content-out-of-precode-tags-or-auto-close-the-tags
..and other such resources and it seems like < confuses TinyMCE and it wants to see &lt;, otherwise it assumes it's a mistake.
Any ideas?
I was able to do a little more research since posting the question and here's what I found...
This gets inserted into the database correctly:
<div class="code_frame">
<div class="inner_code_frame" style="margin-top: 5px;">
<div class="paper">
<pre style="font-family: arial; font-size: 9pt;">
<html>
<table>
<tr>
<td>
hello
</td>
</tr>
</table>
</html>
</pre>
</div>
</div>
</div>
But when TinyMCE renders it after it's been inserted, it's then that the <pre> tag is moved around.It's not that the content as it was entered into the database is wrong. That piece is fine.
But when I go to edit it and ask TinyMCE to reproduce what's in the database, it's then that the <pre> tag gets moved.
I'm looking at this: https://stackoverflow.com/questions/56407393/why-does-tinymce-move-content-out-of-precode-tags-or-auto-close-the-tags
..and other such resources and it seems like < confuses TinyMCE and it wants to see &lt;, otherwise it assumes it's a mistake.
Any ideas?
ASKER
Basically, I want TinyMCE to recognize < and > as legitimate characters and not assume they're mistakes...at least that's where I think the problem lies.
Hi Bruce,
from my example, I don't see anything moved out
from my example, I don't see anything moved out
ASKER
Hey, Leak!
I don't you would see anything moved out...at least not based on what I can see in your example.
Take a look at this...
When I submit some content using this:
...and echo out what the database is receiving from this interface, I get this:
But, when I go to edit it and ask TinyMCE to render what's in the database, it's then that my <pre> tags get moved, and I wind up with this:
Do you smell it?
The characters in the database are accurate, but TinyMCE sees "<" as a mistake and that's when things are getting moved around.
I'm showing you the Source Code of what's in the editor when I ask to see it before I try to edit it, but look at the way it shows up on the screen...
There's no code. Only what TinyMCE assumes is a mistake, so it renders it as a table.
But I think I know how to get around this.
I need to replace all of the < and > characters with &< etc in between the <pre> tags.
I was looking for a script that would accomplish that and I tried this, but it didn't work:
$search = "/[^<pre style=\"font-family: arial; font-size: 9pt;\">](.*)[^<\/pre>]/";
$replace = $super_string;
$string = $row['content'];;
echo preg_replace($search,$replace,$string); */
Any ideas?
I don't you would see anything moved out...at least not based on what I can see in your example.
Take a look at this...
When I submit some content using this:
...and echo out what the database is receiving from this interface, I get this:
<div class=\"code_frame\">\r\n<div class=\"inner_code_frame\" style=\"margin-top: 5px;\">\r\n<div class=\"paper\">\r\n<pre style=\"font-family: arial; font-size: 9pt;\"><!-- your code -->\r\n</pre>\r\n</div>\r\n</div>\r\n</div>
<div class=\"code_frame\">\r\n<div class=\"inner_code_frame\" style=\"margin-top: 5px;\">\r\n<div class=\"paper\">\r\n<pre style=\"font-family: arial; font-size: 9pt;\"><html>\r\n <table>\r\n <tr>\r\n <td>\r\n hello\r\n </td>\r\n </tr>\r\n </table>\r\n </html>\r\n</pre>\r\n</div>\r\n</div>\r\n</div>
...which is exactly right!But, when I go to edit it and ask TinyMCE to render what's in the database, it's then that my <pre> tags get moved, and I wind up with this:
Do you smell it?
The characters in the database are accurate, but TinyMCE sees "<" as a mistake and that's when things are getting moved around.
I'm showing you the Source Code of what's in the editor when I ask to see it before I try to edit it, but look at the way it shows up on the screen...
There's no code. Only what TinyMCE assumes is a mistake, so it renders it as a table.
But I think I know how to get around this.
I need to replace all of the < and > characters with &< etc in between the <pre> tags.
I was looking for a script that would accomplish that and I tried this, but it didn't work:
$search = "/[^<pre style=\"font-family: arial; font-size: 9pt;\">](.*)[^<\/pre>]/";
$replace = $super_string;
$string = $row['content'];;
echo preg_replace($search,$replace,$string); */
Any ideas?
But, when I go to edit it and ask TinyMCE to render what's in the database, it's then that my <pre> tags get moved, and I wind up with this:so it go out well from database but the process that set the tinymce content do something wrong
In my example, the content is the one you're describing
so I'm (and look like you let me alone on that) think you set the tinymce content a different way than me
this is the part of the code missing in your posts : from the database, how do you set the tinymce content??
one more time, in my example I use a string, set the content with that string (the same as your database) get the content out and the string seems to match with what I get out.
ASKER
Leak!
OK, let me explain back to you what you're describing and tell me if I'm correct.
This is what you have on your Fiddle:
So, you're introducing an additional "layer" before you actually insert it into the TinyMCE editor, yes?
OK, I'm going to try that.
One thing...
I'm a pig on roller skates when it comes to certain aspects of JQuery.
You've got this:
<textarea id="desc_area"></textarea>
<div id="html" style="display:none;">
<div class="code_frame">
<div class="inner_code_frame" style="margin-top: 5px;">
<div class="paper">
<pre style="font-family: arial; font-size: 9pt;">
<html>
<table>
<tr>
<td>
hello
</td>
</tr>
</table>
</html>
</pre>
</div>
</div>
</div>
</div>
<script>
let x = tinymce.init ({
selector: '#desc_area',
inline: false,
force_br_newlines: false,
force_p_newlines: true,
forced_root_block: '',
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code insertdatetime media contextmenu'
],
toolbar: 'insertfile undo redo | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | link image'
});
x.then(function(a,b,c) {
tinymce.activeEditor.setContent(document.getElementById("html").innerHTML);
b = tinymce.activeEditor.getContent();
alert(b);
});
</script>
x.then - this is a "promise" dynamic (https://stackoverflow.com/questions/5436327/jquery-deferreds-and-promises-then-vs-done). What you're saying is that if the previous code hasn't gone south, to proceed with the following condition, correct.
You've got three arguments in your function: a, b and c. I don't see "a," although I'm tempted to believe it's the actual editor, yes?
"b" is the portion that's actually getting the content, but I don't understand the "alert." I don't see a pop up on your Fiddle. What is that?
And then, "c..." I don't see that at all.
Can you break it down for me?
OK, let me explain back to you what you're describing and tell me if I'm correct.
This is what you have on your Fiddle:
<textarea id="desc_area"></textarea>
<div id="html" style="display:none;">
<div class="code_frame">
<div class="inner_code_frame" style="margin-top: 5px;">
<div class="paper">
<pre style="font-family: arial; font-size: 9pt;">
<html>
<table>
<tr>
<td>
hello
</td>
</tr>
</table>
</html>
</pre>
</div>
</div>
</div>
</div>
<script>
let x = tinymce.init ({
selector: '#desc_area',
inline: false,
force_br_newlines: false,
force_p_newlines: true,
forced_root_block: '',
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code insertdatetime media contextmenu'
],
toolbar: 'insertfile undo redo | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | link image'
});
x.then(function(a,b,c) {
tinymce.activeEditor.setContent(document.getElementById("html").innerHTML);
b = tinymce.activeEditor.getContent();
alert(b);
});
</script>
So, what you appear to be doing is rendering the content coming from the database in a hidden div. You then grab that content and print it in the TinyMCE editor.So, you're introducing an additional "layer" before you actually insert it into the TinyMCE editor, yes?
OK, I'm going to try that.
One thing...
I'm a pig on roller skates when it comes to certain aspects of JQuery.
You've got this:
<textarea id="desc_area"></textarea>
<div id="html" style="display:none;">
<div class="code_frame">
<div class="inner_code_frame" style="margin-top: 5px;">
<div class="paper">
<pre style="font-family: arial; font-size: 9pt;">
<html>
<table>
<tr>
<td>
hello
</td>
</tr>
</table>
</html>
</pre>
</div>
</div>
</div>
</div>
<script>
let x = tinymce.init ({
selector: '#desc_area',
inline: false,
force_br_newlines: false,
force_p_newlines: true,
forced_root_block: '',
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code insertdatetime media contextmenu'
],
toolbar: 'insertfile undo redo | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | link image'
});
x.then(function(a,b,c) {
tinymce.activeEditor.setContent(document.getElementById("html").innerHTML);
b = tinymce.activeEditor.getContent();
alert(b);
});
</script>
x.then - this is a "promise" dynamic (https://stackoverflow.com/questions/5436327/jquery-deferreds-and-promises-then-vs-done). What you're saying is that if the previous code hasn't gone south, to proceed with the following condition, correct.
You've got three arguments in your function: a, b and c. I don't see "a," although I'm tempted to believe it's the actual editor, yes?
"b" is the portion that's actually getting the content, but I don't understand the "alert." I don't see a pop up on your Fiddle. What is that?
And then, "c..." I don't see that at all.
Can you break it down for me?
ASKER CERTIFIED SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
ASKER
Leak!
That did it! I got it done!
Here's the way it went down...
In the header, I look to see if we're editing a page. If so, we initiate tinyMCE in a way where it knows to grab the content from the hidden div that you suggested. By doing that, all of the previous errors and inconsistencies are solved!
If we're not in "Edit" mode, than a regular startup is initiated and we're good to go.
Just in case anyone else runs into this issue, here's the way it looks:
header.php
Thanks, Leak!
That did it! I got it done!
Here's the way it went down...
In the header, I look to see if we're editing a page. If so, we initiate tinyMCE in a way where it knows to grab the content from the hidden div that you suggested. By doing that, all of the previous errors and inconsistencies are solved!
If we're not in "Edit" mode, than a regular startup is initiated and we're good to go.
Just in case anyone else runs into this issue, here's the way it looks:
header.php
<?php
include ("../carter.inc");
$cxn = mysqli_connect($host,$user,$password,$database)
or die ("couldn't connect to server");
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>Alight Knowledge Transfer Library</title>
<!-- Favicon-->
<link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
<!-- Bootstrap core JS-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bs-custom-file-input/dist/bs-custom-file-input.min.js"></script>
<script type="text/javascript" src="new_tiny/tinymce.min.js"></script>
<?php
/* tinyMCE has a hard time with some of the HTML markup used in the code frames. Hence, anytime you go to edit / display something from the "code_page" table, you first have to display it in a hidden div and then ask tinyMCE to retrieve it from that div. It's just one of those things! You'll see it on the "code_page_display.php" page.
To accommodate that little anomaly, we're going to check to see if we're in Edit mode and see if there's an ID. If so, we'll initiate tinyMCE in a way where it's getting it's content from the "html" div. If not, we'll just do a regular startup.
*/
if ($_GET['Edit'] == "Yes") {
?>
<script>
$(document).ready(function() {
tinymce.init({
theme_advanced_font_sizes: "10px,12px,13px,14px,16px,18px,20px",
font_size_style_values: "12px,13px,14px,16px,18px,20px",
mode:"specific_textareas",
editor_selector: "mceEditor",
paste_data_images: true,
extended_valid_elements : 'script[src|async|defer|type|charset]',
theme: "silver",
height:"350",
setup: function (editor) {
editor.on('init', function (e) {
editor.setContent(document.getElementById("html").innerHTML);
});
},
relative_urls : false,
remove_script_host : true,
plugins: [
"advlist autolink lists link image charmap print preview hr anchor pagebreak",
"searchreplace wordcount visualblocks visualchars code fullscreen",
"insertdatetime media nonbreaking save table directionality",
"emoticons template paste"
],
toolbar1: "insertfile undo redo | styleselect | bold italic | | fontselect | fontsizeselect | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent",
toolbar2: " | link image | print preview media | forecolor backcolor emoticons | paste",
image_advtab: true,
templates: [
{title: 'Test template 1', content: '<b>Test 1</b>'},
{title: 'Test template 2', content: '<em>Test 2</em>'}
],
autosave_ask_before_unload: false
});
});
</script>
<?php } else { ?>
<script>
$(document).ready(function() {
tinymce.init({
theme_advanced_font_sizes: "10px,12px,13px,14px,16px,18px,20px",
font_size_style_values: "12px,13px,14px,16px,18px,20px",
mode:"specific_textareas",
editor_selector: "mceEditor",
paste_data_images: true,
extended_valid_elements : 'script[src|async|defer|type|charset]',
theme: "silver",
height:"350",
relative_urls : false,
remove_script_host : true,
plugins: [
"advlist autolink lists link image charmap print preview hr anchor pagebreak",
"searchreplace wordcount visualblocks visualchars code fullscreen",
"insertdatetime media nonbreaking save table directionality",
"emoticons template paste"
],
toolbar1: "insertfile undo redo | styleselect | bold italic | | fontselect | fontsizeselect | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent",
toolbar2: " | link image | print preview media | forecolor backcolor emoticons | paste",
image_advtab: true,
templates: [
{title: 'Test template 1', content: '<b>Test 1</b>'},
{title: 'Test template 2', content: '<em>Test 2</em>'}
],
autosave_ask_before_unload: false
});
});
</script>
<?php } ?>
<!-- Core theme JS-->
<script src="../js/scripts.js"></script>
<!-- Font Awesome icons (free version)-->
<script src="https://use.fontawesome.com/releases/v5.15.3/js/all.js" crossorigin="anonymous"></script>
<!-- Core theme CSS (includes Bootstrap)-->
<link href="../css/styles.css" rel="stylesheet" />
<link href="../css/special_stylesheet.css" rel="stylesheet" />
</head>
<body>
<?php require_once('navbar.php');?>
Here's the "display.php" page. The thing you want to notice is the hidden div that's grabbing the content as it's coming from the database and than letting that be the source from which tinyMCE is grabbing it's content...<?php
if ($_GET['Edit'] != "Yes")
{
$ID=$_GET[ID];
header("Location: code_page_delete.php?ID=$ID");
exit();
}
else {
require_once('header.php');
$query = "select * from code_page where id = '$_GET[ID]'";
$result = mysqli_query($cxn, $query)
or die ("Couldn't execute query.");
$row = mysqli_fetch_assoc($result);
extract($row);
$str=$row['content'];
$new_str = str_replace("<", "<", $str);
$super_string=str_replace(">", ">", $new_str);
/*
$search = "/[^<pre style=\"font-family: arial; font-size: 9pt;\">](.*)[^<\/pre>]/";
$replace = $super_string;
$string = $row['content'];;
echo preg_replace($search,$replace,$string);
$search = "/[^<tag>](.*)[^<\/tag>]/";
$replace = "your new inner text";
$string = "<tag>i dont know what is here</tag>";
echo preg_replace($search,$replace,$string);
*/
}
?>
<div id="html" style="display:none;"><?php echo $row['content'];?></div>
<!-- Page Content-->
<section class="py-5">
<div class="container">
<div class="row">
<div class="col-md-10 col sm-9">
<p><h5>Page Display</h5></p>
Here's the <a href="../code_page.php?ID=<?php echo $row['id'];?>" target="_blank">page</a> you just selected. Make your changes and click on, "Submit."
<br><br>
<ul>
<li>to view a list of the images that exist in the KTL and retrieve their URL, click <a href="#" data-toggle="modal" data-target="#exampleModalCenter">here</a></li>
<li>to open up the "Code Converter" that takes your HTML code and inserts all of the "<br>" tags so it shows up correctly on your page, click <a href="code_converter.php" target="_blank">here</a></li>
<li>to open up the "Washing Machine," which replaces all of the "<" and ">" characters with "<" and >," click <a href="washing_machine.php" target="_blank">here</a></li>
</ul>
<?php require_once('help.php');?>
<br>
</div>
<div class="col-md-2 col-sm-3 ktl_logo"></div>
</div>
<div class="row">
<div class="col-xs-12" style="width:100%;"><br>
<div style="width:85%; background-color:#ccc; box-shadow:5px 5px 3px #706d6d inset; margin:auto; text-align:center; padding:5px; border-radius:10pt;"><div style="width:100%; background-color:#000; border:1px solid #fff; margin:auto; padding:10px; border-radius:10pt; color:#fff;"><a href="#" data-toggle="modal" data-target="#codeHelps" style="color:#fff; text-decoration:none; font-weight:normal;">click here for helps on how to structure code boxes and other elements...</a></div>
</div>
</div>
</div>
</div>
</section>
<section>
<div class="container">
<div class="row">
<div class="col-xs-12" style="border:1px solid #ccc; box-shadow:5px 5px 3px #ccc; border-radius:10pt; padding:10px; width:85%; margin:auto;">
<form action="code_page_edit.php" method="post">
<div class="form-group">
<label for "name">Code Page Name</label>
<input type="text" class="form-control" name="name" id="term" value="<?php echo $row['page_name'];?>">
</div>
<div class="form-group">
<label for "developer">Developer</label>
<input type="text" class="form-control" name="developer" id="term" value="<?php echo $row['developer'];?>">
</div>
<div class="form-group">
<label for "ticket">Ticket #</label>
<input type="text" class="form-control" name="ticket" id="term" value="<?php echo $row['ticket_number'];?>">
</div>
<div class="form-group">
<label for "url">Ticket URL</label>
<input type="text" class="form-control" name="url" id="url" value="<?php echo $row['url'];?>">
</div>
<div class="form-group">
<label for "keywords">Keywords</label>
<input type="text" class="form-control" name="keywords" id="term" value="<?php echo $row['keywords'];?>">
</div>
<div class="form-group">
<label for "definition">Content</label>
<textarea name="content" id="definition" class="mceEditor"></textarea>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-xs-12"><input type="hidden" name="ID" value="<?php echo $row['id'];?>"><br>
<button type="submit" class="btn btn-dark">Submit</button>
</div>
</div>
</form>
</div>
</section><br>
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">KTL Image List</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" style="height:300px; overflow-y:scroll;">
<table class="table table-borderless">
<tr>
<td style="text-align:center; font-weight:bold;">thumbnail</td>
<td style="text-align:center; font-weight:bold;">image</td>
<td style="text-align:center; font-weight:bold;">url</td>
</tr>
<?php
$querystate = "SELECT * from ktl_images order by name";
$resultstate = mysqli_query($cxn, $querystate)
or die ("Couldn't execute query.");
while ($row=mysqli_fetch_assoc($resultstate)) {
?>
<tr>
<td class="align-middle" style="text-align:center;"><a href="../ktl_images/<?php echo stripslashes($row['url']);?>" target="_blank"><img src="../ktl_images/<?php echo stripslashes($row['url']);?>" style="height:35px;"></a></td>
<td class="align-middle"><?php echo stripslashes($row['name']);?></a>
</td>
<td class="align-middle">ktl_images/<?php echo stripslashes($row['url']);?></td>
</tr>
<?php
}
?>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade in" id="codeHelps" role="dialog" aria-labelledby="searchBoxTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="searchBoxLongTitle">Code Helps</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
To see a comprehensive sample of a "Code Page" that features several elements including JQuery, text boxes and more, click here.
<br><br>
To insert a code frame, use the following:
<br><br>
<span class="blue"><div class="code_frame">
<div class="tab_once" style="font-weight:normal;"><div class="inner_code_frame" style="margin-top: 5px;"></div>
<div class="tab_twice" style="font-weight:normal;"><div class="paper"></div>
<div class="tab_thrice" style="font-weight:normal;"><pre style="font-family: arial; font-size: 9pt;"></div>
<div class="tab_quad" style="font-weight:normal;"><span class="green"><!-- your code --></span></div>
<div class="tab_thrice" style="font-weight:normal;"></pre></div>
<div class="tab_twice" style="font-weight:normal;"></div></div>
<div class="tab_once" style="font-weight:normal;"></div></div>
</div></span>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
tinymce.activeEditor.setContent("<p>Hello world!</p>");
// tinyMCE.activeEditor.setContent(document.getElementById("html").innerHTML);
// alert(document.getElementById("html").innerHTML);
});
</script>
<?php require_once('footer.php');?>
Done!Thanks, Leak!
Open in new window