InnerHTML content is different then actually seen on screen - javascript

I have dynamically created span elements that listen to an onkeyup action.
For example: When a user types into a textbox, it should replace the text inside the <span> too.
lets say I create N elements of span.
N-1 of then works perfectly, but the last one does not.
When I use Inspect Element (Chrome browser) and make the call
document.getElementById("span...").innerHTML
I see the new text, but on screen I still see the old one.
My code:
1. This is the part when i dynamically create the text inputs
titleInput.onkeyup = (function ()
{
var inputObject = titleInput;
var chartIndex = numberOfTds; return function () {
UpdateTitleInMagnifiedView(inputObject, chartIndex);
} })();
UpdateTitleInMagnifiedView - function that updates the span elemnts
document.getElementById("title_magnified_" + chartIndex).innerHTML = inputObject.value;

Related

TinyMCE retrieving elements using DomQuery

I'm dealing with TinyMCE to create a WYSIWYG editor, but there is a problem and now I'm stuck on it.
I need to create a system where users are allowed to create specific documents, each devided into sections i.e. a wrapper. Inside every section there are textual and block elements (p, table, img and so on).
Now, the problem is: when a new section needs to be created I'm using the following code
function insertRawSection () {
// Close the current section and open the next one
Editor.execCommand('mceInsertRawHtml',false,`</section><section><h1>${ZERO_SPACE}</h1>`)
}
This code works, but the real problem comes out when I need to move the cursor at the start of the new h1 element.
I can't retrieve the new heading because if I look for it with DomQuery it doesn't appear.
The code I use to lookup the h1 element is the following
function insertRawSection () {
// Close the current section and open the next one
Editor.execCommand('mceInsertRawHtml',false,`</section><section><h1 data-pointer>${ZERO_SPACE}</h1>`)
// Lookup the last inserted heading
console.log($('[data-pointer]'))
}
Note: The variable $ is not JQuery but is TinyMCE.DomQuery (everything is correctly setted up)
The log print only the previous existent headings, but not the last one. Probably there is somethings like a refresh to execute, but what i have to do in order to "communicate" between this command and the DomQuery APIs?
Instead of using mceInsertRawHtml same can be achieved using dom methods of tinymce.
var ed = tinymce.activeEditor;
var currentNode = ed.selection.getNode();
var newEle = ed.dom.create('section', {}, '<h1></h1>');
ed.dom.insertAfter(newEle, currentNode);
ed.selection.select(newEle.firstChild.firstChild);
ed.selection.collapse(false);
ed.focus();
Once the element is created same can be selected using dom methods to place the cursor at the begining/ending of the element.

Can't understand Javascript eventhandler with for loop code

I'm trying to learn JavaScript and I saw a code to change the css style of a web page depending on the button you press.
I can't understand why or how a for loop indicate witch button was press. Here is the javascript part:
var buttons = document.getElementsByTagName("button");
var len = buttons.length;
for (i = 0; i < len; i++) {
buttons[i].onclick = function() {
var className = this.innerHTML.toLowerCase();
document.body.className = className;
};
}
http://jsfiddle.net/qp9jwwq6/
I looked on the net and w3 school but they don't explain that code with a for loop. Can someone explain it to me?
Thank you
Lets break it down.
First we need to have access to the DOM element on the page, so we do that by using a method on the document itself which will return the element we want to manipulate.
var buttons = document.getElementsByTagName("button");
The buttons var will be a list of ALL the buttons on the page. We want to do something with all of them, so first we cache the length of the list, i.e, count how many buttons we have.
var len = buttons.length;
Then we basically say: set i to 0, and step it up one until its equal to the number of buttons we have.
for (i = 0; i < len; i++) {
Now, to access one button from the list, we need to use the brackets notation. So buttons[0] is the first element, buttons[1] is the second, etc. Since i starts at 0, we put i in the brackets so that on each iteration it will access the next button in the list.
buttons[i].onclick = function() {
var className = this.innerHTML.toLowerCase();
document.body.className = className;
};
}
This is equivalent of doing:
buttons[0].onclick = function() {
var className = this.innerHTML.toLowerCase();
document.body.className = className;
};
buttons[1].onclick = function() {
var className = this.innerHTML.toLowerCase();
document.body.className = className;
};
buttons[2].onclick = function() {
var className = this.innerHTML.toLowerCase();
document.body.className = className;
};
// etc.
But of course that is super inefficient, and we may not know how many buttons the page has. So we get all the buttons there, find out how many there are, then go through each button and assign an event handler to it along with a new class.
Now, looking at the onclick handler itself we can see that it first finds the HTML within the button being clicked, turns it into lowercase letters, and assigns it to a variable:
var className = this.innerHTML.toLowerCase();
By using this we're ensuring that each button will know to get it's own innerHTML when clicked. We're not tracking which button is which, we're just telling each button to check it's own content.
Then what it does is change the class of the body HTML element to whatever it is that we just parsed
document.body.className = className;
So say you have something like
<button>success</button>
<button>failure</button>
<button>warning</button>
Clicking the first button would set the <body> element's class to success, clicking the second would set it to failure, and the third would set it to warning.
First line saves all buttons in a variable called buttons. This is actually an array since there can be several buttons on the page. Then you iterate through each button and define a function which should be executed onclick. Lets say you have 2 buttons then it will be buttons[0] and buttons[1] which get the function.
Firstly, speaking generally, the underlying basis for this code is a little wonky and unusual and non-robust, so don't anticipate that you're on the brink of learning any powerful insight into JavaScript or code design.
On to the answer:
The for-loop does not "indicate" which button was pressed. Rather, it loops through every button element on the page and assigns the exact same function definition to the onclick attribute of each element. The code that ends up running when a particular button element is clicked (here I'm talking about the function body) assigns a CSS class to the body element by assigning to document.body.className.
Your question is asking how the function knows which class name to assign to document.body.className. The function grabs the class name from the innerHTML of the button element, which is accessible as this.innerHTML (because in an event handler, this is a reference to the element on which the triggering event occurred). The HTML <button> element is a little bit special, in that, although it is generally a simple-looking button, it is also a non-leaf node, meaning it contains its own HTML. You can put a lot of things in there, but in this example, they just have a plain text node which consists of exactly (or nearly exactly) the class name (Normal for one and Changed for the other). That's how the function can get a CSS class name that is specific to that button; it grabs it from the text inside the clicked <button> element.
I said "nearly exactly" back there because there's actually a letter-case difference between the button text and the actual CSS classes they've defined in the CSS rules (which are normal and changed). That's why they have to lower the letter-case of the extracted button text (toLowerCase()) before assigning the class name. CSS classes are case-sensitive (see Are CSS selectors case-sensitive?).
As I said, this is unusual code. It is rather inadvisable to create a mapping (especially an inexact mapping!) between plain HTML text and code metadata (CSS classes in this case).

Dynamically created textarea with no .val()

I'm trying to allow users to edit the text of a paragraph in a website. I take a paragraph and replace the <p> tags with <textarea> tags using the .replaceWith() function. When I try to take the value of the textarea, it returns blank. Here's a JSfiddle.
HTML:
<p><a class="edit">Edit</a>I'm going to change this into a textarea field and retrieve the value.</p>
JS:
$(document).ready(function() {
$('.edit').hide();
var object = $('p');
object.on("mouseenter", function() {
$('.edit').show();
object.on('click','.edit',function(){
var oldText = object.text();
oldText = oldText.substr(4); // Exclude the word 'Edit'
object.replaceWith($("<textarea>").val(oldText).css("width",object.css('width')).css('height',object.css('height')));
var value = object.val();
alert("Value: "+value);
});
});
});
I'm a programming beginner, so if you have style or implementation tips, feel free to share. This is just my gut reaction to solving the problem; there may be a simpler way to accomplish the same thing.
EDIT: I should also mention that in my website, each paragraph comes from a database table that I'm displaying using an AJAX function. When the user is done editing, he can click a button, and the website will take the new value of the textarea field and UPDATE *table* SET *text*=newText WHERE *text* LIKE oldText;
Try just using contenteditable='true' instead of changing to a textarea. It will make the <p> editable.
Like this:
<p contenteditable='true'><a class="edit">Edit</a>
I'm going to change this into a textarea field and retrieve the value.</p>
If you want to make your text area editable when someone clicks 'Edit', you can create a function that sets the contenteditable attribute to true and then gives focus to the <p> element.
Your code is not trying to get the value of the <textarea>. Your call:
object.replaceWith( ... )
does not change the value of the variable "object" — it's still the jQuery object for the <p> tag, but after that it's out of the DOM. <p> tags don't have a "value" property.
It's almost always a bad idea to set up event handlers inside another event handler (well, an event handler for interaction events anyway). Event handlers accumulate, so each "mouseenter" event will add another "click" handler.
ckersch is right about an easier method being to use contenteditable, but if you're looking to a solution for your specific problem, change your selector from this:
var value = object.val();
To this:
var value = $("textarea").val();
Full code:
$(document).ready(function() {
$('.edit').hide();
var object = $('p');
object.on("mouseenter", function() {
$('.edit').show();
object.on('click','.edit',function(){
var oldText = object.text();
oldText = oldText.substr(4); // Exclude the word 'Edit'
object.replaceWith($("<textarea>").val(oldText).css("width",object.css('width')).css('height',object.css('height')));
var value = $("textarea").val();
alert("Value: "+value);
});
});
});
Fiddle
There are many ways you could make it more robust, including adding a class or id to your textarea, and then using it to be selected, such as this way:
object.replaceWith($("<textarea class='selectMe'>").val(oldText).css("width",object.css('width')).css('height',object.css('height')));
var value = $(".selectMe").val();
You are using the method replaceWith() wrong. The argument must be a string or a function that returns a string, not a jquery selector. Also, you should place the onclick event outside of the mouseenter event (this is valid for any event, never nest them)
$(document).ready(function() {
function makeTextarea(e) {
e.preventDefault();
var edit = $(e.currentTarget);
var parent = edit.parent();
edit.remove();
parent.replaceWith('<textarea>' + parent.text() + '</textarea>');
}
$('.edit').on('click', makeTextarea);
});
Fiddle: http://jsfiddle.net/U57v2/4/
"When the document is ready listen for clicks on .edit class. When clicked store a reference to the parent element (<p>) and then remove the edit element. Finally replace the parent element (<p>) with a textarea with the contents of the <p> element."
ckersh is absolutely right about the contenteditable, but if you're looking for a specific answer to your code, there are a few things you could improve.
There are a couple of issues with your code. First, you're rebinding the on('click') handler every time you mouse over the paragraph, so if you mouse over 5 times, you're executing the anonymous function 5 times. You only need to bind the on routine once. Second, the variable object never changes, so when you replace it with a textarea, you need a new selector to get the value.
I've updated your fiddle with the enhancements I've mentioned above. I also added a mouseleave event, because I figure you want to hide the "Edit" button when you leave the paragraph. The updated javascript can be seen below:
$(document).ready(function () {
$('.edit').hide();
var object = $('p');
object.on("mouseenter", function () {
$('.edit').show();
}).on("mouseleave", function () {
$('.edit').hide();
}).on("click", '.edit', function () {
var oldText = object.text();
oldText = oldText.substr(4); // Exclude the word 'Edit'
object.replaceWith($("<textarea>").val(oldText).css("width", object.css('width')).css('height', object.css('height')));
var value = $("textarea").val();
alert("Value: " + value);
});
});

Add/Remove elements via onclick

I have a chrome extension written in pure JS which is basically composed of functions which append data to links on the page.
An example of the functions which basically just appends a click integer to the link element passed in from another function:
function addCounttoLinks(link, counts) {
var clicks = counts.split(":")[1].split(",")[0].trim();
var count = document.createTextNode((" (" + clicks + ")"));
var dspan = document.createElement("span");
dspan.appendChild(count);
dspan.style.fontSize = "10px"; dspan.style.textAlign = "center";
link.appendChild(dspan);
}
What is the most efficient way to execute/unexecute these functions, so that I can implement a "on/off" button for the user to click which would add/remove the data appended by the extension. Is the only solution to write functions to remove the previously created elements?
Put a class on the span (say, my-nifty-extension-extra — you want it to be fairly unlikely to clash with something else), then add a class to body (say, hide-my-nifty-extension) when hiding the data, and remove it when showing it. Then use this style rule:
body.hide-my-nifty-extension span.my-nifty-extension-extra {
display: none;
}
E.g., just before your appendChild:
dspan.className = "my-nifty-extension-extra";
And then:
function showExtraData() {
document.body.classList.remove('hide-my-nifty-extension');
}
function hideExtraData() {
document.body.classList.add('hide-my-nifty-extension');
}
(One of the joys of writing a Chrome extension is that you know you have things like classList.)

JavaScript insertBefore not updating - needs double or treble click

This is driving me nuts, a simple script to swap two nodes in a two node ul:
// Html
<input type="button" onclick="swapItems()" value="Swap Items" />
//JavaScript
function swapItems() {
var ul2 = document.getElementById("ul2");
var first = ul2.firstChild;
var last = ul2.lastChild;
ul2.insertBefore(last, first);
}
It works but I have to either double click or triple click the button to see the changed list.
If you look at the childNodes of ul2 it will become apparent. The first child of ul2 is a blank text node, so is the last child. The reason you have to click a few times is that you are moving blank text nodes around. You can't see it on the web page, but if you use a web inspector you should be able to pick it up.
You could change the code to:
function swapItems() {
var ul2 = document.getElementById("ul2");
var liChildren = ul2.getElementsByTagName('li');
var first = ul2.liChildren[0];
var last = ul2.liChildren[liChildren.length - 1];
ul2.insertBefore(last, first);
}
That should work as you wanted.
Remove the whitespaces in #ul2. Whitespaces are also considered as children nodes.

Categories

Resources