I have multiple text-areas that I'd like to enhance using tinyMCE. I can get the text areas to show as the Rich Text Editors by initializing TinyMCE on all text areas in the page as below:
$(function () {
tinymce.init({
selector: "textarea",
statusbar: false,
setup: function (editor) {
editor.on('change', function () {
editor.save();
});
}
});
});
This also handles the sync process between the Tiny editor and the actual textarea.
My html, that populates the text areas looks like this:
<div id="divEditor1" class="container-fieldset">
<div class="editor-label-field" style="left: 50px">
<%:Html.LabelFor(Function(model) model.divEditor1, "divEditor1")%>
</div>
<div class="editor-field-fn">
<%:Html.TextBoxFor(Function(model) model.divEditor1, New With { Key .class = "ucase-input" } )%>
<%:Html.ValidationMessageFor(Function(model) model.divEditor1)%>
</div>
</div>
<div id="divEditor2" class="container-fieldset">
<div class="editor-label-field" style="left: 50px">
<%:Html.LabelFor(Function(model) model.divEditor2, "divEditor2")%>
</div>
<div class="editor-field-fn">
<%:Html.TextBoxFor(Function(model) model.divEditor2, New With { Key .class = "ucase-input" } )%>
<%:Html.ValidationMessageFor(Function(model) model.divEditor2)%>
</div>
</div>
... etc
I can read the content from the TinyMCE editors like this:
for (var i = 0; i < numberOfEditors; i++) {
sFieldValue = document.getElementById("FormFieldText" + i).value;
//sFieldValue = tinyMCE.get("FormFieldText" + i).getContent(); -> or like this, works just as well.
};
The problem I am having is getting the TinyMCE editor box to display the already existing text on page load (text read from a database), since it always shows up as an empty text box. However, the text is imported correctly in the original textarea in the background. Formatted and escaped, since it goes through some ajax calls.
I've seen I can set the content of tiny like this:
tinyMCE.get('my_editor').setContent(data);
However, I need to do it programmatically, in a way that all textareas on my page export the information to tiny. Just like the above
setup: function (editor) {
editor.on('change', function () {
editor.save();
});
}
Also, what would be the right time to un-encode the text so tiny can read it? (I assume this step is necessary)
If you initialize TinyMCE on each textarea before each textarea has its content then TinyMCE won't auto-magically go back and update itself when the textarea gets updated with content - it just does not work that way.
You could use TinyMCE APIs (as you already know from your post) to update the editor's content once you get the data. Alternatively you could delay initializing TinyMCE until after the data is fetched into each textarea.
Neither approach is better/worse - there are certainly multiple ways to solve this and they all work if implemented appropriately.
I ended up parsing the list of active tinymce instances by ID and populating each one with the corresponding text from the textareas in the background.
I came across this post looking for a solution to get the data from the database back into tinymce, and I ended up with
<textarea id="textarea"><?= htmlspecialchars($description); ?></textarea>
Just using php and the variable from the array from the database.
Related
We have a website hosted at hubspot, we use their native WYSIWYG to design layouts then style them with css and js.
On the homepage http://www.lspatents.com/ it used to have a form under the "Get started here" title, it had around 10 questions, and used javascript to split them to steps so they can fit in the same area on the currently shown blank box.
It was working just fine till two days ago the form disappeared and left it with a blank area as you can see now, and as far as i know no one has touched this code recently.
Here is the js code that was used to manipulate the form
// Hero Form
$(window).load(function() {
// disable autocomplete to fix bug
$('.hero-form form').attr("autocomplete", "off");
$('.hero-form .hs-richtext').each(function() {
$(this).nextUntil('.hs-richtext').wrapAll('<div class="step" />');
});
// Hide Loading icon
$('.hero-form form').css('background', 'none');
$('.hero-form form .step:nth-of-type(2)').show();
// First Step to Second Step
$('.step').find('.hs-richtext').change(function() {
$('.step:nth-of-type(2)').hide().next().next().fadeIn();
});
// Second Step to Third Step
$('.step').find('.hs-input').change(function() {
var names = {};
$(':radio').each(function() {
names[$(this).attr('name')] = true;
});
var count = 0;
$.each(names, function() {
count++;
});
if ($(':radio:checked').length === count) {
$('.step:nth-of-type(4)').hide().next().next().fadeIn();
}
});
});
As far as i was able to tell, the developer used css to hide the whole form area with display:none; and used the js above to split the questions to steps and show a certain number in each step.
You can see the code being called in the footer so there is no problem with the link to the .js file, also if you inspect the element and disable the display:none; that's declared for any of the divs within the hero-form all questions get displayed, so there is no problem with the form either, so why has it stopped working?
Would appreciate any help,
This line will no longer work with your mark-up...
$('.hero-form form .step:nth-of-type(2)').show();
There are a number of additional divs that wrap your mark-up, placed there by react, React has placed a series of div inside your form which are being hidden by your existing CSS (which I assume used to just be a series of STEP's)
The CSS that hides the nodes is :
.hero-form form>div, .hero-form form>.step {
display: none;
}
The nodes that are being hidden with display:none
<div data-reactid=".0.0:$1">
<div class="hs-richtext" data-reactid=".0.0:$1.0">
<hr>
</div>
<div class="step">
<div class="hs_patent field hs-form-field" data-reactid=".0.0:$1.$patent">
<label placeholder="Enter your Do you have a patent?" for="patent-9fc8dd30-a174-43bd-be4a-34bd3a00437e_2496" data-reactid=".0.0:$1.$patent.0">
<span data-reactid=".0.0:$1.$patent.0.0">Do you have a patent?</span>
<span class="hs-form-required" data-reactid=".0.0:$1.$patent.0.1">*</span>
</label>
<div class="hs-field-desc" style="display:none;" data-reactid=".0.0:$1.$patent.1">
</div>
</div>
Your JQuery will add display:block to the DIV with the class 'step' bit wont alter the parent DIV (inserted by React) which still prevents your node from being shown.
You need to alter you JQuery to call show() on the parent() that contains the "step" div you wish to show.
Please check your browser console ans see you have problem loading this form:
https://forms.hubspot.com/embed/v3/form/457238/9fc8dd30-a174-43bd-be4a-34bd3a00437e
and this is the error:
net::ERR_NAME_RESOLUTION_FAILED
It's better you change your DNS to something like 8.8.8.8 and see if the problem still exists or not.
How to setup a div to see the instant result of html by making the div contenteditable. Like
<div id="edit" contenteditable="true"></div>
<button id="trigger">Apply code</button>
Here, div id="edit" assumed to be editor and result viewer as well itself. e.g. write code on div then click on "Apply code" button and the result of html code written in div should be appeared within the same div (i.e. div id="edit").
html
<div id="edit" contenteditable="true"></div>
<button id="trigger" onclick="applyCode()">Apply code</button>
js
function applyCode() {
$('#edit').html($('#edit').html());
}
So here's the basic setup of what you're trying to do...
<div id='editor' contenteditable></div>
<button id='trigger' value='apply'>
And the JS code:
document.getElementById('trigger').addEventListener('click', (function(dv)
{
return function()
{
dv.innerHTML = dv.textContent;
};
}(document.getElementById('editor'))), false);
Here's a fiddle.
But play around with it, and you'll soon find out that there's a problem with it: Try adding markup tags, and edit the input a second time. That's not going to work as well as you'd hope.
Also check out the resulting DOM, after the user puts in some multi-line input: new divs, paragraphs and what have you are added. You'll have to preserve that markup, using something like:
document.querySelector('#editor').addEventListener('click', function()
{
var currentMarkup = this.innerHTML;
}, false);
Then, you'll have to set about sharing that innerHTML with the trigger event handler, and find a way to check if the user changed the actual markup. Have fun with that...
I suggest you use tools that have already been written, tried and tested, instead of setting about building your own.
It'll take you too long, and you'll end up with something that's not even half as capable as the existing editors out there.
Check this related post for details: Replace words of text area
First you need to create an input box where you can type your html code.
<div id="edit"><textarea id='editor'></textarea></div>
Then there is the button, which 'renders' the code.
<button id="trigger">Apply code</button>
Next, you need to create a javascript code which changes the content of '#edit' to '#editor' 's value.
var edit = document.getElementById('edit');
var editor = document.getElementById('editor');
var trigger = document.getElementById('trigger');
trigger.onclick = function() {
edit.innerHTML = editor.value;
};
Fiddle: http://jsfiddle.net/W75Ut/1/
<div id="editable" contenteditable="true">aaaaaaa</div>
<br/>
<button onclick="texttohtml()">Click to apply Html</button>
<script>
function texttohtml()
{
var t=$("#editable").text();
$("#editable").html(t);
}
</script>
I have a DropDownList where onChange sets the content of the TextArea which is my CKEditor control.
When the editor is not in use I run this bit of code for onChange:
$(".setBody").change(function() {
//
var className = "." + $(this).attr("sExternalId");
var text = $(this).val();
//
$(className).val(text);
});
I'm not very experienced with Javascript/JQuery and I just can't figure out how to perform the same using CKEditor's setData() function. This is what I've tried:
$(".setCKBody").change(function() {
//
var className = "." + $(this).attr("sExternalId");
var text = $(this).val();
//
var editor = $(className).ckeditorGet();
editor.setData(text, function() {
alert("The content was set");
});
});
$(".setCKBody").change(function() {
//
var className = "." + $(this).attr("sExternalId");
var text = $(this).val();
//
CKEDITOR.instances[$(className)].setData(text, function() {
alert("The content was set");
});
});
Am I close? I think one of the main limitations is that I have multiple editor controls with the same id and name, only the class can tell them apart which is why I'm using that with the JQuery. I've tried working through some examples online, but I'm not sure how to apply them to this scenario - that's probably my inexperience coming through there...
Thanks.
EDIT
This is how my textarea and dropdownlist appears in view source:
<textarea class="editArea M3" cols="20" id="Body" name="Body" rows="5">
*some text*
</textarea>
<select class="setCKBody" id="Templates" name="Templates" sExternalId="M3">
<option value="some value">Option 1</option>
<option value="some value">Option 2</option>
</select>
The onChange event above is triggered from the dropDownList changing and is linked to the textArea via the "sExternalId" attribute. I realised I used "id" as the attribute name in the example above which was in error, so I changed that.
I use this to set it as a CKEditor control:
<script>CKEDITOR.replaceAll('editArea');</script>
I have between 2 to 6 textarea controls on the same page, created with razor like this:
#Html.TextAreaFor(m => m.Body, new { #class = "span12 editArea " + Model.ExternalId, rows = 5 })
They are contained within a partial view that is used like this:
#foreach (MailTemplateModel oTemplate in Model.Templates)
{
#Html.Partial("_MailPartial", oTemplate)
}
This is why each text area has "Body" set as the id and name. I think this is the heart of the problem, with there being multiple elements with the same id and name CKEditor is not able to select the correct one. I've tried to do CKEDITOR.instances["className"] but that was undefined, whereas doing CKEDITOR.instances.Body did work, but would only ever return the same value.
I'm going to restructure the way the page is created to avoid this, hopefully my issues will be solved at the same time.
Here's a few pointers.
Use class="foo" if you have many things that you refer to as a group, like like here it looks like you would have many setCKBody elements you listen to for change events.
Use id="foo" if you have one single specific thing.
Using the same id and class for one element usually is not the right thing to do.
CKEDITOR.instances[xxx] <-- xxx should be a string, not a jquery object - so CKEDITOR.instances[className] might work better (I can't say not having seen your HTML).
It would help if we saw your HTML; textarea definitions and setCKBody definitions. Do you have many ckeditors and many setCKBody elements?
My original approach to this scenario was all wrong, I had a model that contained multiple mail templates and so I rendered each one via a partial view within the same page so that the user could click to edit any one of them and the details would appear in a modal popup - within the same window. What I wanted to avoid was forcing the user to navigate to another window to edit a mail template, but this lead to multiple elements having the same id and name attributes which prevented me from accessing them correctly.
I've now added a list box where the user can select a template to edit, the selected template is rendered underneath and so avoids the multiple name and id issue. Now I know there is only ever 1 CKEditor control so I can access it in my js like this:
var editor = CKEDITOR.instances.SelectedTemplate_Body;
SelectedTemplate_Body is the name and id of the element I made into a CKEditor control. The onChange function I wrote for the dropdownlist is now written like this:
$(document).ready(function() {
//
$(".setBody").change(function() {
//
var templateId = $(this).val();
//
$.ajax({
type: "GET",
url: msHost + "MailTemplates/UpdateBody",
data: { "templateId": templateId },
cache: false,
dataType: "text",
success: function (data) {
CKEDITOR.instances.SelectedTemplate_Body.setData(data);
}
})
});
});
The tempalteId attribute is the value associated to the dropdownlist selection, this lets me know which template to use for setting the content of my editor control.
MailTemplates/UpdateBody points to a public method in my MailTemplates controller which runs a search on available mail templates and matches against the template Id passed in, the method then returns the body of the template as a string.
public string UpdateBody(string tempalteId)
{
TemplateQuery oQuery;
//
oQuery = new TemplateQuery();
oQuery.Execute();
foreach (MailTemplate oTemplate in oQuery.Results)
if (oTemplate.Id.Equals(templateId))
return oTemplate.Body;
//
return string.Empty;
}
This line replaces the contents of the CKEditor control with the response from the controller method.
CKEDITOR.instances.SelectedTemplate_Body.setData(data);
Thanks #Nenotlep for trying to help out, you gave me a few things to think about there.
I'm a beginner on Javascript/TinyMCE and I try to understand how is it possible to get the HTML content from the editor, and show it with a simple alert() function.
I've this minimalist config on my HTML page :
<div id="tiny">
<script type="text/javascript">
tinyMCE.init({
// General options
mode : "specific_textareas",
editor_selector : "mceEditor"
});
</script>
</div>
<form method="post" action="somepage">
<textarea id="myarea1" class="mceEditor">This will be an editor.</textarea>
</form>
On the TinyMCE Website, They explained that i have to use this :
// Get the HTML contents of the currently active editor
console.debug(tinyMCE.activeEditor.getContent());
And here too
tinymce.activeEditor.getContent()
I don't know why it doesn't work
Someone have an idea ?
I don't know why it doesn't work
It's not working because
console.debug(tinyMCE.activeEditor.getContent());
tinymce.activeEditor.getContent();
these statements are not being executed.
Try to follow this FIDDLE ....
tinyMCE.init({
// General options
mode : "specific_textareas",
editor_selector : "mceEditor"
});
Function for getting content ....
function get_editor_content() {
// Get the HTML contents of the currently active editor
console.debug(tinyMCE.activeEditor.getContent());
//method1 getting the content of the active editor
alert(tinyMCE.activeEditor.getContent());
//method2 getting the content by id of a particular textarea
alert(tinyMCE.get('myarea1').getContent());
}
Get the content of editor on button click ...
<button onclick="get_editor_content()">Get content</button>
Maybe it's the case? Your variable is tinyMCE, but you are calling getContent() on tinymce. JS is case sensitive ;)
I was looking for a solution and tried some of the above then went looking more on the tinymce documentation and found this to be effective.
Using the tiny mce 4
function getHTML()
{
tinymce.activeEditor.on('GetContent', function(e) {
console.log(e.content);
});
}
just call that function with an onclick and see what the results are...
My source is:
http://www.tinymce.com/wiki.php/api4:class.tinymce.ContentEvent
TinyMCE creates an iframe under the format '#textareaid'+'_ifr' So by using jquery we can query the HTML contents of the text area you like
the iframe id will be your textarea Id with "_ifr" appended to that. So you can extract HTML contents from tinyMce
$('#textareaId_ifr').contents().find("html").html();
I want to replace all {baseurl} keywords to proper url in TinyMCE editor. How can i do that?
For example if user will add HTML in editor <img src="{baseurl}/image.jpg" /> i want to see this image in TinyMCE editor - so this will be replaced to <img src="http://mydomain.com
/image.jpg" />
Any ideas?
Here is the code that will replace your editor content. But you will need to do this action at the correct time.
var editor = tinymce.get('my_editor_id'); // use your own editor id here - equals the id of your textarea
var content = editor.getContent();
content = content.replace(/{\$baseurl}/g, 'http://mydomain.com');
editor.setContent(content);
With this solution I was able to modify the content on-the-fly, without replacing the content as whole:
tinymce.init({
setup: (editor)=>{
editor.on('init', ()=>{
$(editor.contentDocument).find('a').prop('title', 'my new title');
});
}
});
Maybe it helps someone :)
I used a very simple code working good with me
tinymce.get("page-content").setContent(''); // 'page-content' as the textarea id
tinymce.get("page-content").execCommand('mceInsertContent', !1, 'New content data');