Is it possible to focus on textarea after selecting option? - javascript

After adding text inside textarea i would like to focus caret just after this input. Unfortunately I can't make function focus() to be called. Although when I use focus() on click it works perfectly
Here is my function:
function append_textarea(textarea, select, div_for_select) {
$(textarea).on('keyup paste cut mouseup', function (event) {
var contentHeight = $(this).textareaHelper('height');
$(this).height(contentHeight);
var obj = $(this).textareaHelper('caretPos');
var left = obj.left + 15;
var top = obj.top + 50;
if (event.which === 219) {
$(div_for_select).attr('style', 'top: ' + top + 'px;' + ' left: ' + left + 'px;').fadeIn();
openDropdown(select);
} else if (event.which === 221) {
$(div_for_select).fadeOut('slow');
}
});
$(div_for_select).on('keydown click', function (event) {
if (event.which === 13 || event.which === 1) {
var $txt = $(textarea);
var caretPos = $txt[0].selectionStart;
var textAreaTxt = $txt.val();
var txtToAdd = $(select).val();
$txt.val(textAreaTxt.substring(0, caretPos) + txtToAdd + textAreaTxt.substring(caretPos)).focus();
$(this).fadeOut('slow');
}
});
$(textarea).keyup();
function openDropdown(elementId) {
function down() {
var pos = $(this).offset(); // remember position
var len = $(this).find("option").length;
if (len > 20) {
len = 20;
}
$(this).css("position", "absolute");
$(this).css("zIndex", 9999);
$(this).offset(pos); // reset position
$(this).attr("size", len); // open dropdown
$(this).unbind("focus", down);
$(this).trigger('focus');
}
function up() {
$(this).css("position", "static");
$(this).attr("size", "1"); // close dropdown
$(this).unbind("change", up);
$(this).trigger('focus')
}
$(elementId).focus(down).blur(up).focus();
}
}
append_textarea('#example', '#select', '.tail');
$('#select').on('click', function () {
$('#example').focus();
});
$('#click').on('click', function () {
$('#example').focus();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<style>
.tail {
position: absolute;
}
textarea {
min-width: 250px;
min-height: 100px;
}
</style>
<!-- #MAIN PANEL -->
<div id="main" role="main">
<!-- RIBBON -->
<div id="ribbon">
<!-- breadcrumb -->
<ol class="breadcrumb">
<li>Home</li>
</ol>
</div>
<!-- col -->
<div class="col-xs-12 col-sm-7 col-md-7 col-lg-4">
<h1 class="page-title txt-color-blueDark"></h1>
<div class="clearfix"></div>
<textarea id="example"></textarea>
<div class="tail" hidden>
<select id="select">
<option style="display: none;">Please select one</option>
<option value="USER_NAME}">First and Last Name</option>
<option value="WO_NO}">Work Order Number</option>
<option value="PROD}">Product Name and Size</option>
<option value="PROJECT}">Project Name</option>
<option value="MACHINE}">Machine Name</option>
</select>
</div>
<div>
<input type="button" value="CLICK ME!" id="click">
</div>
</div>
</div>
<script src="https://rawgit.com/Codecademy/textarea-helper/master/textarea-helper.js"></script>

Related

How to apply condition on document.getElementsByClassName

I continue to discover JavaScript and again I am faced with a small problem
In my order form, I have about 300 items and each item is wrapped by a div, in which there is a class color1
I would like when the quantity is greater than 0 inside my div that my div turns green.
It works great with a div.
But, if I submit the same class for my second item and the quantity of my item is greater than 0, my div does not turn green unless I add for example the color3 class
To better understand,
here is the extract of my code where I retrieve the quantity of the article and where I test this quantity, in order to give it the green color if quantity greater than 0.
In summary, what I want is that all the div or the whenitee is greater than 0 green silk.
$('.ajouter-panier').click(function(event) {
event.preventDefault();
var nom_option = "";
var prix_option = 0;
var url = $(this).data('url');
var option_checkbox = $(this).data('checkbox');
if (option_checkbox != "") {
var checkboxes = document.getElementsByClassName(option_checkbox);
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked == true) {
var nom_option = nom_option + " (" + $(checkboxes[i]).data('nom') + ")";
var prix_option = prix_option + Number($(checkboxes[i]).data('prix'));
}
}
}
if ($(this).data('select')) {
var nom = $(this).data('nom') + " (" + document.getElementById("" + $(this).data('select') + "").value + ")" + nom_option;
} else var nom = $(this).data('nom');
var prix = Number($(this).data('prix')) + (prix_option);
if ($(this).attr('data-qte')) {
var qte_option = $(this).attr('data-qte');
MonPanier.ajouter_produit_dans_panier(nom, prix, qte_option, url);
} else MonPanier.ajouter_produit_dans_panier(nom, prix, 1, url);
var color = $(this).attr('data-qte');
console.log(color);
if (color > 0) {
const collection1 = document.getElementsByClassName("couleur1");
collection1[0].style.backgroundColor = "green";
const collection2 = document.getElementsByClassName("couleur2");
collection2[0].style.backgroundColor = "green";
} else {
const collection1 = document.getElementsByClassName("couleur1");
collection1[0].style.backgroundColor = "";
const collection2 = document.getElementsByClassName("couleur2");
collection2[0].style.backgroundColor = "";
}
afficherpanier();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 couleur1" style="margin-bottom: 15px; padding-left: 5px; padding-right: 5px;">
<div class="card">
<div class="">
<h3 class="card-title centrer_titre_texte couleur2">2001</h3>
<div class="card-body stylecardbody" style="padding-top: 0;">
<div class="row mt-12">
<div class="col-md-12">
<select class="form-select styleselect" aria-label="2001" onchange="changeQte(this);">
<option selected value="1.10">1 sachet </option>
<option value="2">2 sachets</option>
<option value="3">3 sachets </option>
</select>
</div>
<a style="cursor: pointer; margin-bottom: 5px;width: 90%;display: block;margin-left: auto;margin-right: auto;" data-nom="2001" data-prix="1.10" data-qte="1" data-checkbox="2001" data-url="https://phil.pecheperle.be/image-perles/perle-verre-peche-gardon-2001.JPG"
class="btn btn-primary ajouter-panier b-items__item__add-to-cart" onclick="setTimeout(() => ouvreMaJolieAlert(event), 1000);"> ajouter au panier </a>
</div>
</div>
</div>
</div>
</div>
You should parse value of attribute to number.
var color = Number($(this).attr('data-qte'));
If you want to change <a> parent to color:green; on event onclick and if is set attribute (data-qte > 0) and apply it to multiple divs with class couleur1. Then the simpliest way to do this is to go to that element using event.currentTarget and parentElement
const collection1 = event.currentTarget.parentElement.parentElement.parentElement.parentElement.parentElement
if (color > 0) {
collection1.style.backgroundColor = "green";
} else {
collection1.style.backgroundColor = "";
}
full code:
$('.ajouter-panier').click(function (event) {
event.preventDefault();
var nom_option = "";
var prix_option = 0;
var url = $(this).data('url');
var option_checkbox = $(this).data('checkbox');
if (option_checkbox != "") {
var checkboxes = document.getElementsByClassName(option_checkbox);
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked == true) {
var nom_option = nom_option + " (" + $(checkboxes[i]).data('nom') + ")";
var prix_option = prix_option + Number($(checkboxes[i]).data('prix'));
}
}
}
if ($(this).data('select')) {
var nom = $(this).data('nom') + " (" + document.getElementById("" + $(this).data('select') + "").value + ")" + nom_option;
} else var nom = $(this).data('nom');
var prix = Number($(this).data('prix')) + (prix_option);
if ($(this).attr('data-qte')) {
var qte_option = $(this).attr('data-qte');
} else MonPanier.ajouter_produit_dans_panier(nom, prix, 1, url);
var color = Number($(this).attr('data-qte'));
const collection1 = event.currentTarget.parentElement.parentElement.parentElement.parentElement.parentElement
const h1 = event.currentTarget.parentElement.parentElement.previousElementSibling
if (color > 0) {
collection1.style.backgroundColor = "green";
h1.style.color = "green";
} else {
collection1.style.backgroundColor = "";
h1.style.color = "";
}
afficherpanier();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.1/dist/js/bootstrap.bundle.min.js"></script>
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 couleur1"
style="margin-bottom: 15px; padding-left: 5px; padding-right: 5px;">
<div class="card">
<div class="">
<h3 class="card-title centrer_titre_texte couleur2">2001</h3>
<div class="card-body stylecardbody" style="padding-top: 0;">
<div class="row mt-12">
<div class="col-md-12">
<select class="form-select styleselect" aria-label="2001" onchange="changeQte(this);">
<option selected value="1.10">1 sachet </option>
<option value="2">2 sachets</option>
<option value="3">3 sachets </option>
</select>
</div>
<a style="cursor: pointer; margin-bottom: 5px;width: 90%;display: block;margin-left: auto;margin-right: auto;"
data-nom="2001" data-prix="1.10" data-qte="1" data-checkbox="2001"
data-url="https://phil.pecheperle.be/image-perles/perle-verre-peche-gardon-2001.JPG"
class="btn btn-primary ajouter-panier b-items__item__add-to-cart"
onclick="setTimeout(() => ouvreMaJolieAlert(event), 1000);"> ajouter au panier </a>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 couleur1"
style="margin-bottom: 15px; padding-left: 5px; padding-right: 5px;">
<div class="card">
<div class="">
<h3 class="card-title centrer_titre_texte couleur2">2001</h3>
<div class="card-body stylecardbody" style="padding-top: 0;">
<div class="row mt-12">
<div class="col-md-12">
<select class="form-select styleselect" aria-label="2001" onchange="changeQte(this);">
<option selected value="1.10">1 sachet </option>
<option value="2">2 sachets</option>
<option value="3">3 sachets </option>
</select>
</div>
<a style="cursor: pointer; margin-bottom: 5px;width: 90%;display: block;margin-left: auto;margin-right: auto;"
data-nom="2001" data-prix="1.10" data-qte="1" data-checkbox="2001"
data-url="https://phil.pecheperle.be/image-perles/perle-verre-peche-gardon-2001.JPG"
class="btn btn-primary ajouter-panier b-items__item__add-to-cart"
onclick="setTimeout(() => ouvreMaJolieAlert(event), 1000);"> ajouter au panier </a>
</div>
</div>
</div>
</div>
</div>

Cloning div on click breaks when moving the button out of container?

I found this nifty js fiddle and it does nearly exactly what I need
However its cloning the parent of the button and id like to have the button separate from the actual div being cloned. (if you put the clone button back into the container with the remove button it works fine again)
In all I am trying to accomplish 3 things.
1. Have the button outside of the div that's being duplicated (1 button)
2. Limit the number of duplication's to a total of 6. (or any changeable variable)
3. Update the <h4> content and change the number 1 to the next number. ie: (1-6)
I'm not very JS savvy although I do dabble. If anyone has the time to help me figure out the above it would be beyond appreciated! Here's the JS FIDDLE I've been playing with.
Thanks!
var regex = /^(.+?)(\d+)$/i;
var cloneIndex = $(".clonedInput").length;
function clone(){
$(this).parents(".clonedInput").clone()
.appendTo("body")
.attr("id", "clonedInput" + cloneIndex)
.find("*")
.each(function() {
var id = this.id || "";
var match = id.match(regex) || [];
if (match.length == 3) {
this.id = match[1] + (cloneIndex);
}
})
.on('click', 'button.clone', clone)
.on('click', 'button.remove', remove);
cloneIndex++;
}
function remove(){
$(this).parents(".clonedInput").remove();
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
I think the folowing is what you're trying to acheive, you have to add another variables cloned_nbr and clones_limit to control the cloned divs:
var cloneIndex = 1;
var clones_limit = 4;
var cloned_nbr = $(".clonedInput").length-1; //Exclude Default (first) div
function clone()
{
if(cloned_nbr<clones_limit)
{
cloneIndex++;
cloned_nbr++;
var new_clone = $(".clonedInput").first().clone();
new_clone.attr("id", "clonedInput" + cloneIndex);
new_clone.find(".label-nbr").text(cloneIndex);
new_clone.find(".category").attr("id","category"+cloneIndex);
new_clone.find(".remove").attr("id","remove"+cloneIndex);
new_clone.on('click', 'button.clone', clone);
new_clone.on('click', 'button.remove', remove);
$(".clone").before(new_clone);
}
}
function remove(){
if(cloneIndex>1){
$(this).parents(".clonedInput").remove();
cloned_nbr--;
}
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
body { padding: 10px;}
.clonedInput { padding: 10px; border-radius: 5px; background-color: #def; margin-bottom: 10px; }
.clonedInput div { margin: 5px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clonedInput1" class="clonedInput">
<div>
<label for="txtCategory" class="">Learning category <span class="label-nbr">1</span><span class="requiredField">*</span></label>
<select class="category" name="txtCategory[]" id="category1">
<option value="">Please select</option>
</select>
</div>
<div class="actions">
<button class="remove">Remove</button>
</div>
</div>
<button class="clone">Clone</button>
You can select the last occurence of .clonedInput and clone that, then insert it after the original element:
var regex = /^(.+?)(\d+)$/i;
function clone(){
var cloneIndex = $(".clonedInput").length + 1;
if (cloneIndex > 6) return;
$source = $(".clonedInput").last();
$source.clone()
.insertAfter($source)
.attr("id", "clonedInput" + cloneIndex)
.find("*")
.each(function() {
var id = this.id || "";
var match = id.match(regex) || [];
if (match.length == 3) {
this.id = match[1] + (cloneIndex);
}
})
.on('click', 'button.remove', remove)
.find('label').html('Learning category ' + cloneIndex + ' <span class="requiredField">*</span>');
}
function remove(){
$(this).parents(".clonedInput").remove();
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clonedInput1" class="clonedInput">
<div>
<label for="txtCategory" class="">Learning category 1 <span class="requiredField">*</span></label>
<select class="" name="txtCategory[]" id="category1">
<option value="">Please select</option>
</select>
</div>
<div class="actions">
<button class="remove">Remove</button>
</div>
</div>
<button class="clone">Clone</button>
I'd initialize plain block as template and use it as clone base.
HTML
<div class="box-wrap">
<button class="clone">Clone</button>
</div>
Full demo: http://jsfiddle.net/jeafgilbert/tfFLt/1898/

Jquery add and remove DIV tag row based on select value

I found this great stackoverflow post that does what I need: add and remove rows based on a select value, but the only difference is that in my case, I don't have a table but blocks of divs. In the table example it works fine, but with divs it fails.
I've been using the console to try to fix it to no avail. It seems there are problems with the index value and the increment, but I don't undertand why with a table row works but with divs it doesnot. Could anyone take a look?
Here's a jsfiddle that shows the issue: http://jsfiddle.net/njes3w1a/1/
This is my script:
if ($('#returnRequest').length) {
var row_i = 0;
function emptyRow() {
row_i++;
this.obj = $('<div class="return-row control-group row"></div>');
this.obj.append('<div class="col-md-6"><label class="control-label">Serial number</label><input type="text" class="form-control" value=""/></div><div class="col-md-4"><label class="control-label">Item is</label><select class="form-control"><option value="">Select</option><option value="1">New and unopened</option><option value="2">Defective</option></select></div>');
}
function refresh(new_count) {
//how many applications we have drawed now ?
console.log("New count= " + new_count);
if (new_count > 0) {
$("#noa_header").show();
} else {
$("#noa_header").hide();
}
var old_count = parseInt($('#productRowWrapper').children().length);
console.log("Old count= " + old_count);
//the difference, we need to add or remove ?
var rows_difference = parseInt(new_count) - old_count;
console.log("Rows diff= " + rows_difference);
//if we have rows to add
if (rows_difference > 0) {
console.log('enetered a');
for (var i = 0; i < rows_difference; i++)
$('#productRowWrapper').append((new emptyRow()).obj);
} else if (rows_difference < 0) //we need to remove rows ..
{
console.log('enetered b');
var index_start = old_count + rows_difference + 1;
console.log("Index start= " + index_start);
$('.return-row:gt(' + index_start + ')').remove();
console.log(row_i);
console.log(rows_difference);
row_i += rows_difference;
console.log(row_i);
}
}
$('#quantityReturn').change(function () {
refresh($(this).val());
});
}
I would use this code:
if ($('#returnRequest').length) {
function emptyRow() {
this.obj = $('<div class="return-row control-group row"></div>');
this.obj.append(
'<input type="text" class="form-control" value=""/>' +
'<select class="form-control">' +
'<option value="">Select</option>' +
'<option value="1">New and unopened</option>' +
'<option value="2">Defective</option>' +
'</select>');
}
function refresh(count) {
if (count > 0) {
$("#noa_header").show();
} else {
$("#noa_header").hide();
}
var wrapper = $('#productRowWrapper');
while (wrapper.children().length > count)
wrapper.children().last().remove();
while (wrapper.children().length < count)
wrapper.append((new emptyRow()).obj);
}
$('#quantityReturn').change(function () {
refresh(parseInt($(this).val()));
});
}
It does not introduce unnecessary variables and it a bit shorter. Updated fiddle at http://jsfiddle.net/njes3w1a/6/.
you were calculating your strt index wrong add this
var index_start =old_count- Math.abs(rows_difference)-1 ;
Code
if ($('#returnRequest').length) {
var row_i = 0;
function emptyRow() {
row_i++;
this.obj = $('<div class="return-row control-group row"></div>');
this.obj.append('<input type="text" class="form-control" value=""/><select class="form-control"><option value="">Select</option><option value="1">New and unopened</option><option value="2">Defective</option></select>');
}
function refresh(new_count) {
//how many applications we have drawed now ?
console.log("New count= " + new_count);
if (new_count > 0) {
$("#noa_header").show();
} else {
$("#noa_header").hide();
}
var old_count = parseInt($('#productRowWrapper').children().length);
console.log("Old count= " + old_count);
//the difference, we need to add or remove ?
var rows_difference = parseInt(new_count) - old_count;
console.log("Rows diff= " + rows_difference);
//if we have rows to add
if (rows_difference > 0) {
console.log('enetered a');
for (var i = 0; i < rows_difference; i++)
$('#productRowWrapper').append((new emptyRow()).obj);
} else if (rows_difference < 0) //we need to remove rows ..
{
console.log('enetered b');
var index_start =old_count- Math.abs(rows_difference)-1 ;
console.log("Index start= " + index_start);
console.log('.return-row:gt(' + index_start + ')');
$('.return-row:gt(' + index_start + ')').remove();
console.log(row_i);
console.log(rows_difference);
row_i += rows_difference;
console.log(row_i);
}
}
$('#quantityReturn').change(function () {
refresh($(this).val());
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="" id="returnRequest">
<div class="form-group row">
<p class="col-sm-3 control-label">* Quantity to return</p>
<div class="col-sm-9">
<div class="row control-group">
<div class="col-sm-6 control-item" id="signupForename">
<select class="form-control" id="quantityReturn">
<option value="0">Please select</option>
<option value="1">1 item</option>
<option value="2">2 items</option>
<option value="3">3 items</option>
<option value="4">4 items</option>
</select>
</div>
</div>
</div>
</div>
<div class="panel panel-info">
<div class="pane-body">
<div class="form-group row">
<div class="col-sm-9">
<div id="noa_header" style="display: none;">Header</div>
<div id="productRowWrapper"></div>
</div>
</div>
</div>
</div>
</div>

how to create a list item and remove it when using increment and de-increment buttons

On my page, I have a section for extra hardware. I have images, beside an increment and de-increment buttons. Right now when you click on the image, a list item appears and when you click off that image it the list item is removed. However, I want to be able to add/update and or remove the list items with the increment and de-increment buttons. I'm just not sure how to put the code together to do so...
Here is the fiddle: http://jsfiddle.net/lolsen7/e2LSB/10/
Here is the html:
<div class="maincontent wraps">
<div class="text-area">
<div class="gethardware">
<div id="station-builder-2">
<h3 class="margin-15 margin-top-30">Need Extra?</h3>
<p>Click on the on the buttons(+/-) to add or subtract from your order.</p>
<div class="numbers-row ipad">
<label for="name">Portable Terminals(iPad mini(s))</label>
<img id="ipad" class="part" src="http://placehold.it/100x100" alt="iPad_mini" />
<input type="text" name="portable-terminals" id="ipad_1" value="0" />
</div>
<div class="numbers-row mobile">
<label for="name">Wireless Receipt Printers</label>
<img id="mobilePrinter" class="part" src="http://placehold.it/100x100" alt="Mobile Printer" />
<input type="text" name="wireless" id="mobile_1" value="0" />
</div>
<!--<div class="numbers-row printer2">
<label for="name">Receipt Printers - For Food/Beverage Orders</label>
<img id="printer2" class="part" src="http://placehold.it/100x100" alt="Receipt Printer2" />
<input type="text" name="printer" id="printer_2" value="0" />
</div>-->
<ul id="list"></ul>
</div>
<!--end of gethardware-->
</div>
<!--end of text area-->
</div>
<!--end of main content-->
Here is the Javascript:
//JS FOR HARDWARE SECTION
$ //JS FOR HARDWARE SECTION
$(document).ready(function () {
$(".part").mouseover(function () {
if (this.className !== 'part selected') {
$(this).attr('src', 'http://placehold.it/100x100' + this.id + 'http://placehold.it/100x100/ffffff');
}
$(this).mouseout(function () {
if (this.className !== 'part selected') {
$(this).attr('src', 'http://placehold.it/100x100' + this.id + 'http://placehold.it/100x100');
}
});
});
var list = document.getElementById("list");
$(".part").click(function () {
if (this.className == 'part') {
$(this).attr('src', 'http://placehold.it/100x100' + this.id + 'http://placehold.it/100x100');
console.log(this);
//START OF THE EXTRA -- STATION 2
if (this.id == "ipad") {
li = document.createElement('li');
li.setAttribute('id', 'ipad_li');
li.appendChild(document.createTextNode('iPad mini (portable terminal'));
list.appendChild(li);
}
if (this.id == "mobilePrinter") {
li = document.createElement('li');
li.setAttribute('id', 'mobile_li');
li.appendChild(document.createTextNode('Mobile Printer'));
list.appendChild(li);
}
if (this.id == "printer2") {
li = document.createElement("li");
li.setAttribute("id", "printer2_li");
li.appendChild(document.createTextNode("Receipt Printer"));
list.appendChild(li);
}
} else {
$(this).attr('src', 'http://placehold.it/100x100' + this.id + 'http://placehold.it/100x100/ffffff');
console.log(this);
if (this.id == "ipad") {
$('#ipad_li').remove();
}
if (this.id == "mobilePrinter") {
$('#mobile_li').remove();
}
if (this.id == "printer2") {
$("#printer2_li").remove();
}
}
$(this).toggleClass('selected');
});
//javascript for the buttons to increment and decrement
$(".numbers-row").append('<div class="inc buttons">+</div><div class="dec buttons">-</div>');
$(".buttons").on("click", function () {
var $buttons = $(this);
var oldValue = $buttons.parent().find("input").val();
if ($buttons.text() == "+") {
var newVal = parseFloat(oldValue) + 1;
if (newVal > 2) {
return;
}
} else {
// Don't allow decrementing below zero
if (oldValue > 0) {
var newVal = parseFloat(oldValue) - 1;
} else {
newVal = 0;
}
}
$buttons.parent().find("input").val(newVal);
});
});

Autocomplete suggestions within Rich Text Editor

I'm presently working on a project to get a rich text editor up and running that allows for autocompletion suggestions to come up at the cursor's location after a token key is pressed, for example '#'. I have gotten this working inside of a textarea without issue through jQuery, but it won't play nice inside of a RTE iFrame.
The autocomplete example I have works through AJAX to scan a server file to check the text being typed for autocomplete 'tags'.
Editor from Mozilla: www-archive.mozilla.org/editor/midasdemo/
Autocomplete from: www.amirharel.com/2011/03/07/implementing-autocomplete-jquery-plugin-for-textarea/
<html>
<head>
<style type="text/css">
.imagebutton {height: 22; width: 23; border: solid 2px #C0C0C0; background-color: #C0C0C0}
.image {position: relative; left: 1; top: 1; height:20; width:21; border:none;}
.toolbar {height: 30; background-color: #C0C0C0;}
</style>
<script>
var command = "";
function InitToolbarButtons() {
var kids = document.getElementsByTagName('DIV');
for (var i=0; i < kids.length; i++) {
if (kids[i].className == "imagebutton") {
kids[i].onmouseover = tbmouseover;
kids[i].onmouseout = tbmouseout;
kids[i].onmousedown = tbmousedown;
kids[i].onmouseup = tbmouseup;
kids[i].onclick = tbclick;
}
}
}
function tbmousedown(e)
{
var evt = e ? e : window.event;
this.firstChild.style.left = 2;
this.firstChild.style.top = 2;
this.style.border="inset 2px";
if (evt.returnValue) {
evt.returnValue = false;
} else if (evt.preventDefault) {
evt.preventDefault( );
} else {
return false;
}
}
function tbmouseup()
{
this.firstChild.style.left = 1;
this.firstChild.style.top = 1;
this.style.border="outset 2px";
}
function tbmouseout()
{
this.style.border="solid 2px #C0C0C0";
}
function tbmouseover()
{
this.style.border="outset 2px";
}
function insertNodeAtSelection(win, insertNode)
{
// get current selection
var sel = win.getSelection();
// get the first range of the selection
// (there's almost always only one range)
var range = sel.getRangeAt(0);
// deselect everything
sel.removeAllRanges();
// remove content of current selection from document
range.deleteContents();
// get location of current selection
var container = range.startContainer;
var pos = range.startOffset;
// make a new range for the new selection
range=document.createRange();
if (container.nodeType==3 && insertNode.nodeType==3) {
// if we insert text in a textnode, do optimized insertion
container.insertData(pos, insertNode.nodeValue);
// put cursor after inserted text
range.setEnd(container, pos+insertNode.length);
range.setStart(container, pos+insertNode.length);
} else {
var afterNode;
if (container.nodeType==3) {
// when inserting into a textnode
// we create 2 new textnodes
// and put the insertNode in between
var textNode = container;
container = textNode.parentNode;
var text = textNode.nodeValue;
// text before the split
var textBefore = text.substr(0,pos);
// text after the split
var textAfter = text.substr(pos);
var beforeNode = document.createTextNode(textBefore);
afterNode = document.createTextNode(textAfter);
// insert the 3 new nodes before the old one
container.insertBefore(afterNode, textNode);
container.insertBefore(insertNode, afterNode);
container.insertBefore(beforeNode, insertNode);
// remove the old node
container.removeChild(textNode);
} else {
// else simply insert the node
afterNode = container.childNodes[pos];
container.insertBefore(insertNode, afterNode);
}
range.setEnd(afterNode, 0);
range.setStart(afterNode, 0);
}
sel.addRange(range);
};
function getOffsetTop(elm) {
var mOffsetTop = elm.offsetTop;
var mOffsetParent = elm.offsetParent;
while(mOffsetParent){
mOffsetTop += mOffsetParent.offsetTop;
mOffsetParent = mOffsetParent.offsetParent;
}
return mOffsetTop;
}
function getOffsetLeft(elm) {
var mOffsetLeft = elm.offsetLeft;
var mOffsetParent = elm.offsetParent;
while(mOffsetParent){
mOffsetLeft += mOffsetParent.offsetLeft;
mOffsetParent = mOffsetParent.offsetParent;
}
return mOffsetLeft;
}
function tbclick()
{
if ((this.id == "forecolor") || (this.id == "hilitecolor")) {
parent.command = this.id;
buttonElement = document.getElementById(this.id);
document.getElementById("colorpalette").style.left = getOffsetLeft(buttonElement);
document.getElementById("colorpalette").style.top = getOffsetTop(buttonElement) + buttonElement.offsetHeight;
document.getElementById("colorpalette").style.visibility="visible";
} else if (this.id == "createlink") {
var szURL = prompt("Enter a URL:", "http://");
if ((szURL != null) && (szURL != "")) {
document.getElementById('edit').contentWindow.document.execCommand("CreateLink",false,szURL);
}
} else if (this.id == "createimage") {
imagePath = prompt('Enter Image URL:', 'http://');
if ((imagePath != null) && (imagePath != "")) {
document.getElementById('edit').contentWindow.document.execCommand('InsertImage', false, imagePath);
}
} else if (this.id == "createtable") {
e = document.getElementById("edit");
rowstext = prompt("enter rows");
colstext = prompt("enter cols");
rows = parseInt(rowstext);
cols = parseInt(colstext);
if ((rows > 0) && (cols > 0)) {
table = e.contentWindow.document.createElement("table");
table.setAttribute("border", "1");
table.setAttribute("cellpadding", "2");
table.setAttribute("cellspacing", "2");
tbody = e.contentWindow.document.createElement("tbody");
for (var i=0; i < rows; i++) {
tr =e.contentWindow.document.createElement("tr");
for (var j=0; j < cols; j++) {
td =e.contentWindow.document.createElement("td");
br =e.contentWindow.document.createElement("br");
td.appendChild(br);
tr.appendChild(td);
}
tbody.appendChild(tr);
}
table.appendChild(tbody);
insertNodeAtSelection(e.contentWindow, table);
}
} else {
document.getElementById('edit').contentWindow.document.execCommand(this.id, false, null);
}
}
function Select(selectname)
{
var cursel = document.getElementById(selectname).selectedIndex;
/* First one is always a label */
if (cursel != 0) {
var selected = document.getElementById(selectname).options[cursel].value;
document.getElementById('edit').contentWindow.document.execCommand(selectname, false, selected);
document.getElementById(selectname).selectedIndex = 0;
}
document.getElementById("edit").contentWindow.focus();
}
function dismisscolorpalette()
{
document.getElementById("colorpalette").style.visibility="hidden";
}
function Start() {
document.getElementById('edit').contentWindow.document.designMode = "on";
try {
document.getElementById('edit').contentWindow.document.execCommand("undo", false, null);
} catch (e) {
alert("This demo is not supported on your browser.");
}
InitToolbarButtons();
if (document.addEventListener) {
document.addEventListener("mousedown", dismisscolorpalette, true);
document.getElementById("edit").contentWindow.document.addEventListener("mousedown", dismisscolorpalette, true);
document.addEventListener("keypress", dismisscolorpalette, true);
document.getElementById("edit").contentWindow.document.addEventListener("keypress", dismisscolorpalette, true);
} else if (document.attachEvent) {
document.attachEvent("mousedown", dismisscolorpalette, true);
document.getElementById("edit").contentWindow.document.attachEvent("mousedown", dismisscolorpalette, true);
document.attachEvent("keypress", dismisscolorpalette, true);
document.getElementById("edit").contentWindow.document.attachEvent("keypress", dismisscolorpalette, true);
}
}
</script>
<style type="text/css" style="display:none">ul.auto-list{display:none;position:absolute;top:0px;left:0px;border:1px solid green;background-color:#A3DF99;padding:0;margin:0;list-style:none;}ul.auto-list>li:hover,ul.auto-list>li[data-selected=true]{background-color:#236574;}ul.auto-list>li{border:1px solid gray;cursor:default;padding:2px;}mark{font-weight:bold;}</style>
<style type="text/css">#ta{width:300px;height :100px;font-size:11px;font-family:"Helvetica Neue",Arial,sans-serif;white-space:pre;}</style>
<script src="js/jquery-1.5.js" type="text/javascript"></script>
<script src="js/autocomplete.js" type="text/javascript"></script>
<script type="text/javascript">
var tags = [];
function initEditorTextarea(){
$.ajax("autotags.txt",{
success: function(data, textStatus, jqXHR){
tags = data.replace(/\r/g, "" ).split("\n");
$("#editor textarea").autocomplete({
wordCount:1,
on: {
query: function(text,cb){
var words = [];
for( var i=0; i<tags.length; i++ ){
if( tags[i].toLowerCase().indexOf(text.toLowerCase()) == 0 ) words.push(tags[i]);
if( words.length > 5 ) break;
}
cb(words);
}
}
});
}
});
}
$(document).ready(function(){
Start();
initEditorTextarea();
});
</script>
</head>
<body>
<div id="editor">
<textarea style="width: 750px; height: 150px; margin-bottom: 10px;"></textarea>
</div>
<table bgcolor="#C0C0C0" id="toolbar">
<tr>
<td>
<select id="formatblock" onchange="Select(this.id);">
<option value="<p>">Normal</option>
<option value="<p>">Paragraph</option>
<option value="<h1>">Heading 1 <H1></option>
<option value="<h2>">Heading 2 <H2></option>
<option value="<h3>">Heading 3 <H3></option>
<option value="<h4>">Heading 4 <H4></option>
<option value="<h5>">Heading 5 <H5></option>
<option value="<h6>">Heading 6 <H6></option>
<option value="<address>">Address <ADDR></option>
<option value="<pre>">Formatted <PRE></option>
</select>
</td>
<td>
<select id="fontname" onchange="Select(this.id);">
<option value="Font">Font</option>
<option value="Arial">Arial</option>
<option value="Courier">Courier</option>
<option value="Times New Roman">Times New Roman</option>
</select>
</td>
<td>
<select unselectable="on" id="fontsize" onchange="Select(this.id);">
<option value="Size">Size</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
</select>
</td>
<td>
<div class="imagebutton" id="bold"><img class="image" src="images/bold.gif" alt="Bold" title="Bold"></div>
</td>
<td>
<div class="imagebutton" id="italic"><img class="image" src="images/italic.gif" alt="Italic" title="Italic"></div>
</td>
<td>
<div class="imagebutton" id="underline"><img class="image" src="images/underline.gif" alt="Underline" title="Underline"></div>
</td>
<td>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="forecolor"><img class="image" src="images/forecolor.gif" alt="Text Color" title="Text Color"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="hilitecolor"><img class="image" src="images/backcolor.gif" alt="Background Color" title="Background Color"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="justifyleft"><img class="image" src="images/justifyleft.gif" alt="Align Left" title="Align Left"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="justifycenter"><img class="image" src="images/justifycenter.gif" alt="Center" title="Center"></div>
</td>
<td>
<div style="left: 70;" class="imagebutton" id="justifyright"><img class="image" src="images/justifyright.gif" alt="Align Right" title="Align Right"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="insertorderedlist"><img class="image" src="images/orderedlist.gif" alt="Ordered List" title="Ordered List"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="insertunorderedlist"><img class="image" src="images/unorderedlist.gif" alt="Unordered List" title="Unordered List"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="outdent"><img class="image" src="images/outdent.gif" alt="Outdent" title="Outdent"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="indent"><img class="image" src="images/indent.gif" alt="Indent" title="Indent"></div>
<td>
<div style="left: 10;" class="imagebutton" id="createlink"><img class="image" src="images/link.gif" alt="Insert Link" title="Insert Link"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="createimage"><img class="image" src="images/image.gif" alt="Insert Image" title="Insert Image"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="createtable"><img class="image" src="images/table.gif" alt="Insert Table" title="Insert Table"></div>
</td>
</tr>
</table>
<br>
<iframe id="edit" width="100%" height="400px"></iframe>
<iframe width="250" height="170" id="colorpalette" src="images/colors.html" style="visibility:hidden; position: absolute;"></iframe>
<script>
function viewsource(source)
{
var html;
if (source) {
html = document.createTextNode(document.getElementById('edit').contentWindow.document.body.innerHTML);
document.getElementById('edit').contentWindow.document.body.innerHTML = "";
html = document.getElementById('edit').contentWindow.document.importNode(html,false);
document.getElementById('edit').contentWindow.document.body.appendChild(html);
document.getElementById("toolbar").style.visibility="hidden";
} else {
html = document.getElementById('edit').contentWindow.document.body.ownerDocument.createRange();
html.selectNodeContents(document.getElementById('edit').contentWindow.document.body);
document.getElementById('edit').contentWindow.document.body.innerHTML = html.toString();
document.getElementById("toolbar").style.visibility="visible";
}
}
function usecss(source)
{
document.getElementById('edit').contentWindow.document.execCommand("useCSS", false, !(source));
}
function readonly(source)
{
document.getElementById('edit').contentWindow.document.execCommand("readonly", false, !(source));
}
</script>
<input type="checkbox" onclick="viewsource(this.checked)">
View HTML Source</input>
</body>
</html>
I've spent a lot of time looking at browser based solutions and JS based options but none of them have worked yet. The best example I can find of what I'm trying to accomplish is the Confluence RTE environment: https://confluence.atlassian.com/display/CONF33/Using+Autocomplete+in+the+Rich+Text+Editor
I don't need anything super fancy, just the ability to run a WYSIWYG sort of basic rich text editor with the ability to autocomplete on a specific key input. How should I approach getting this to work?
EDIT: Had to change the links above the code as I'm new here so they wouldn't automatically be linked.

Categories