I want to add a custom button that sets up a bootstrap accordian element. To do this, I will need to know how many other accordian elements are in the text already so that I can give the new element the proper ID. Right now I have:
// Add a custom button
ed.addButton('accordianArea', {
title : 'Accordian Area',
text : '[accordian/]',
icon: false,
onclick : function() {
// Add you own code to execute something on click
ed.focus();
var text = ed.selection.getContent({'format': 'html'});
var accordianText = '<div class="panel panel-default">' +
'<div class="panel-heading" role="tab" id="heading1">' +
'<h4 class="panel-title">' +
'<a role="button" data-toggle="collapse" href="#heading1" aria-expanded="true" aria-controls="heading1">' +
'Accordian Title<span id="_cursor" />' +
'</a>' +
'</h4>' +
'</div>' +
'<div id="heading1" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="heading1">' +
'<div class="panel-body">' +
(text ? text : 'Accordian Text') +
'</div>' +
'</div>' +
'</div>';
ed.execCommand('mceInsertContent', false, accordianText);
ed.selection.select(ed.dom.select('#_cursor')[0]); //select the inserted element
ed.selection.collapse(0); //collapses the selection to the end of the range, so the cursor is after the inserted element
ed.dom.remove('_cursor'); //remove the element
}
});
I need to be able to count how many panel-collapse classes are in the text so I can make heading1 and collapse1 increment accordingly with each new add. Also I suppose I will need to be able to edit the ones already there in case one is removed, so that the next one added is not a duplicate of a different one.
Is there a way count these in either jQuery or javascript?
Thanks,
James
Yes, this is possible:
var ed = tinymce.get('your_editor_id');
var $elements = $(ed.getBody()).find('.panel-collapse');
var count = $elements.length;
Related
I am making a to do list. Each list item includes a remove button when created.
But I cannot access these remove buttons in my script, because it is not included in my DOM, although I used '.appendChild'. Can anyone help?
const buttonSubmit = document.querySelector('#button-submit');
const form = document.querySelector('form');
const icons = document.querySelector('#icons');
let toDoList = document.querySelector('#todolist');
const input = document.querySelector('#formtext');
form.addEventListener('submit', (e) => {
e.preventDefault();
const newListItem = document.createElement('li');
newListItem.innerHTML = '<span>' + input.value + '</span>' +
'<span id="icons">' +
'<button id="check" class="buttonlist">' + '<img src="checked.png" alt="">' + '</button>' +
'<button id="remove" class="buttonlist">' + '<img src="remove.png" alt="">' + '</button>' +
'<button id="edit" class="buttonlist">' + '<img src="edit.png" alt="">' + '</button>' + '</span>';
toDoList.appendChild(newListItem);
form.reset();
})
const buttonCheck = document.querySelector('#check');
const buttonEdit = document.querySelector('#edit');
const buttonRemove = document.getElementById('remove');
buttonRemove.addEventListener('click', function() {
alert('remove list item');
})
<div class="container-box">
<h1>To Do List</h1>
<br>
<form action="">
<input type="text" id="formtext" name="formtext">
<button id="button-submit">Add Item</button>
</form>
<br><br><br><br>
<!-- Dynamic list here -->
<ul id="todolist"> </ul>
</div>
Problems
ids must be unique, every time you add a task to the list -- after the first one everything is invalid HTML. When directed to an id the browser will find the first id then stop and ignore the duplicate ids. Use class and/or name attributes for any replicated tags.
The reason why the remove button doesn't work is because the reference to the button was defined when it didn't exist.
Figure I
// After page is loaded...
const buttonRemove = document.getElementById('remove');
// Console will tell you buttonRemove is null
// User has not entered any data nor has user clicked the add button
Moreover, even if that was fixed by referencing the button after it was created, binding it as shown on Figure II will only work for the first button only.
Figure II
buttonRemove.addEventListener('click', function() {
alert('remove list item'); // Don't use alert() use console.log()
})
Solution
Reference tags after they are created. In the OP (Original Post), the contents of the <li> is rendered htmlString which makes referencing newly created tags problematic plus binding to dynamically created tags individually should be avoided if it's feasible and practical in which in most cases it is.
To handle events for an unknown amount of dynamically created tags, bind the event to a static ancestor tag, which in the OP is <ul> or any of it's parent tags (even <body>, document, and window but it's best to be as close as possible). Then make it so the event handler controls which tags respond and how. See Appendix located at the very end of this answer for more details.
There are two examples:
Example A - revised OP code
Example B - a todo list using HTMLFormElement interface, see Appendix
Both examples have commented step-by-step details
Example A
// Reference <form>, <ul>, and <input>
const form = document.querySelector('form');
const list = document.querySelector('ul');
const input = document.querySelector('#text');
// Bind <form> to submit event
form.addEventListener('submit', (e) => {
// Stop default behavior of <form> during submit ecent
e.preventDefault();
// Create <li> and <output>
const item = document.createElement('li');
const out = document.createElement('output');
// Assign value of <input> to the value of <output>
out.value = input.value;
// Add <output> to <li> -- <li> to <ul>
item.append(out);
list.append(item);
/*
Run a for loop 3 times -- on each iteration...
...create an <input> and assign type as "button"...
...buttons [name] and [value] is determined by current index...
...add button to <li>
*/
for (let i = 0; i < 3; i++) {
let btn = document.createElement('input');
btn.type = 'button';
let cmd = i === 0 ? 'done' : i === 1 ? 'edit' : i === 2 ? 'remove' : false;
btn.name = cmd;
btn.value = cmd;
item.append(btn);
}
// Reset <form>
form.reset();
});
// Bind <ul> to click event
list.addEventListener('click', manageList);
// Event handler always passes event object by default
function manageList(e) {
// Reference the tag user clicked
const clk = e.target;
// If user clicked a remove button remove it's parent tag
if (clk.name === 'remove') {
clk.parentElement.remove();
}
if (clk.name === 'edit') {
console.log('EDIT');
}
if (clk.name === 'done') {
console.log('DONE');
}
}
li {
display: flex;
align-items: center
}
[type='button'] {
text-transform: capitalize
}
<form>
<input id="text" name="text" type="text">
<button>Add Item</button>
</form>
<br>
<ul></ul>
Example B
// Bind <form> to click event
document.forms.todo.onclick = taskList;
// Event handler akways passes the event object
function taskList(e) {
// Reference the tag user clicked
const clk = e.target;
// Reference all form controls
const IO = this.elements;
/*
If the user clicked the add button...
...reference the <ul>...
...create <li> and <output>...
...add text from <input> to <output>...
...add <output> to <li>...
...Run a for loop 3 times -- on each iteration...
...create an <button> and assign type as "button"...
...buttons [name] and text is determined by current index...
...add button to <li>...
...add <li> to <ul>...
...clear <input>
*/
if (clk.name == 'add') {
const list = IO.list.firstElementChild;
const item = document.createElement('li');
const text = document.createElement('output');
text.value = IO.data.value;
item.append(text);
for (let i = 0; i < 3; i++) {
let btn = document.createElement('button');
btn.type = 'button';
let cmd = i === 0 ? 'done' : i === 1 ? 'edit' : i === 2 ? 'remove' : false;
btn.name = cmd;
btn.textContent = cmd;
item.append(btn);
}
list.append(item);
IO.data.value = '';
}
/*
If the user clicked a remove button...
...find the <li> ancestor of remove button and remove
it thereby removing the <output> and itself as well
*/
if (clk.name === 'remove') {
clk.closest('li').remove();
}
if (clk.name === 'done') {
console.log('DONE');
}
if (clk.name === 'edit') {
console.log('EDIT');
}
}
<form id='todo'>
<input id='data' required><button name='add' type='button'>Add</button>
<fieldset id='list'>
<ul></ul>
</fieldset>
</form>
Appendix
Events
Event delegation
HTMLFormElement
HTMLFormControlsCollection
Form Controls
Hi I'm trying to add event listener to dynamically created "<a></a>" element, but with no success.
Here is the item/element:
var item = "<div class='item' id='" + id + "'>"
+ "<div class='cell ion-chevron-right'>"
+ "<span class='cell items'>" + text + "</span>"
+ "<a id='" + id + "_icon' class='mIcon ion-ios-close-outline' style='margin-left:-22px; font-size:18px;'></a>"
+ "</div>"
+ "<div class='cell'>" + price + "</div>"
+ "</div>";
return item;
}
And here I insert it into the HTML:
var addAccessorie = function(id, text, price){
var m = id + "_accessorie";
dojo.place(newAccesorieItem(m, text, price)
,"maintenance_accessorie"
,"after"); // before/after
}
How can I add a CLICK event to the "<a>" in DOJO?
Use dojo/dom-construct to create the <a> element and then use dojo/on to bind the onclick event.
var aElement= domConstruct.toDom("<a id='" + id + "_icon' class='mIcon ion-ios-close-outline'
style='margin-left:-22px; font-size:18px;'></a>");
on(aElement, 'click', function(){
//Your click handler
});
If you keep your code exactly as it is,
you can do:
var addAccessorie = function(id, text, price){
var m = id + "_accessorie";
dojo.place(newAccesorieItem(m, text, price)
,"maintenance_accessorie"
,"after"); // before/after
on(dom.byId(m + '_icon'), 'click', function(){
//what needs to be done when <a> is clicked
});
}
Only need to add is a require for dojo/dom and dojo/on
after clicking on a div I am generating a textarea. After then I am loading tinyMCE on it. This works well but after loading the tinyMCE I want to set focus on it using "mceFocus". This isn't working. No focus set regardless which browser I try.
$.when(
btn.parent().html('<textarea autocomplete="off" name="' + fld + '" id="' + fld + '" readonly></textarea>')
).done(function() {
tinyMCE.execCommand('mceAddControl', false, fld);
tinyMCE.execCommand('mceFocus', false, fld);
});
I'm using the twitter block in tumblr which displays the latest tweets from my twitter feed. Right now it displays the last 20 tweets but I want it to only show the last 5 tweets. anyone have any idea how I can do that?
the code I'm using right now is below. I tried changing the .length variable to 5 in the loop, but that didn't do anything:
{block:Twitter}
<div id="twitter" style="display:none;">
<h3>Latest Tweets</h3>
<div id="tweets"></div>
</div>
<script type="text/javascript">
function recent_tweets(data) {
for (i=0; i<data.length; i++) {
document.getElementById("tweets").innerHTML =
document.getElementById("tweets").innerHTML +
'<a href="http://twitter.com/{TwitterUsername}/status/' +
(data[i].id_str ? data[i].id_str : data[i].id) +
'"><div class="content">' + data[i].text +
'</div></a>';
}
document.getElementById("twitter").style.display = 'block';
}
</script>
{/block:Twitter}
Not sure how you changed the variable, but this should work:
<script type="text/javascript">
function recent_tweets(data) {
for (i=0; i<5; i++) {
document.getElementById("tweets").innerHTML =
document.getElementById("tweets").innerHTML +
'<a href="http://twitter.com/{TwitterUsername}/status/' +
(data[i].id_str ? data[i].id_str : data[i].id) +
'"><div class="content">' + data[i].text +
'</div></a>';
}
document.getElementById("twitter").style.display = 'block';
}
</script>
Update
You'll also need to remove the 2nd recent_tweets function that your are calling. The one you change to be i<5 is being overwritten by another one being called later in your theme file.
I have a html page with a form.
The form has Div which gets populated dynamically with Input elements like text box,radio,checkbox etc.
Now I want to retrieve the values of these dynamically created elements in the Html page,so that i can submit it to a page.
//HTML PAGE
<script type="text/javascript">
$(function() {
populateQuestions();
});
$("#submit_btn").click(function() {
// validate and process form here
//HOW TO ??retrieve values???
var optionSelected = $("input#OptionSelected_1").val();// doesn't work?
// alert(optionSelected);
postAnswer(qid,values);//submit values
showNextQuestion() ;// populate Div Questions again new values
});
</script>
<form action="" name="frmQuestion">
<div id="Questions" style="color: #FF0000">
</div>
//Question DIV generation script example radio buttons
//questionText text of question
//option for question questionOptions
// **sample call**
var question = createQuestionElement("1","MCQ", "WHAT IS ABCD??", "Opt1$Opt2$Opt3");
question.appendTo($('#Questions'));
function createQuestionElement(id, type, questionText, questionOptions) {
var questionDiv = $('<div>').attr('id', 'Question');
var divTitle = $('<div>').attr('id', 'Question Title').html(questionText);
divTitle.appendTo(questionDiv);
var divOptions = $('<div>').attr('id', 'Question Options');
createOptions(id, "radio", questionOptions, divOptions);
divOptions.appendTo(questionDiv);
return questionDiv;
}
function createOptions(id, type, options, div) {
var optionArray = options.split("$");
// Loop over each value in the array.
$.each(
optionArray, function(intIndex, objValue) {
if (intIndex == 0) {
div.append($("<input type='" + type + "' name='OptionSelected_" + id + "' checked='checked' value='" + objValue + "'>"));
} else {
div.append($("<input type='" + type + "' name='OptionSelected_" + id + "' value='" + objValue + "'>"));
}
div.append(objValue);
div.append("<br/>");
}
You are creating the same input multiple times, since you're splitting the option array, you're making this:
<input type='radio' name='OptionSelected_1' checked='checked' value='Opt1'>
<input type='radio' name='OptionSelected_1' value='Opt2'>
<input type='radio' name='OptionSelected_1' value='Opt3'>
You are currently fetching by ID with the #, instead you want to fetch by name and get the :checked item, like this:
var optionSelected = $("input[name=OptionSelected_1]:checked").val();