Suppose I have a specific table selected in TinyMCE, like this:
var ed = tinyMCE.activeEditor;
var selection = ed.selection.getContent();
var element = ed.dom.getParent(ed.selection.getNode(), 'table');
How do I loop through the tr elements inside this?
I suspect one of these methods might be relevant, but I'm so new to classes, I'm having trouble understanding how to apply them:
TinyMCE select(): http://www.tinymce.com/wiki.php/API3:method.tinymce.dom.DOMUtils.select
TinyMCE getAll(): http://www.tinymce.com/wiki.php/API3:method.tinymce.html.Node.getAll
You may loop through any node in tinymce like a regular html node because they are in fact regular html nodes.
So this will suffice:
var ed = tinyMCE.activeEditor;
var element = ed.dom.getParent(ed.selection.getNode(), 'table');
var child = element.firstChild;
while(child){
if(child.nodeName.toLowerCase() == 'tr'){
//do your stuff here
}
child = child.nextSibling;
}
Doesn't var element has property childNodes? It's an array of immediate child elements. Each of those will further have properties, where you would be interested in nodeName. Make a recursive function to search (each node further has childNodes), until you find that nodeName=="TR".
BTW, this would be a lot easier with jQuery, if you're interested.
http://www.w3schools.com/htmldom/dom_methods.asp
http://www.w3schools.com/htmldom/dom_nodes_info.asp
Related
I'm having trouble doing a function in javascript
like she takes the form data and lists it below
I'm doing it this way
<pre>
> function changeText2 () {
> var assembly = ['staff', 'description']
> var node = document.createElement ("LI");
> var textnode = document.createTextNode (assembly);
> node.appendChild (textnode);
document.getElementById ("demo"). appendChild (node);
</pre>
only it returns only ids "staff" and "description"
I want you to take several input
An example
what pretty much what I want you to do is this
only with several campuses
Change it to something like this below. Don't forget to modify HTML accordingly as written in JSfiddle link
const list = document.getElementById('demo');
const changeText2= () => {
let staff = document.getElementById('staff').value;
let desc = document.getElementById('desc').value;
let node = document.createElement ("LI");
let textnode = document.createTextNode(`${staff}, ${desc}`);
node.appendChild (textnode);
document.getElementById ("demo"). appendChild (node);
}
https://jsfiddle.net/1gjs789f/
To expand on your code, first you didn't specify whether you want inputs taken from the fields specified within HTML DOM. You do that by separately calling getElementById on specific fields (in this case, 'staff' and 'desc', as per your code example) and call .value function on them to get the value from the fields.
After that, configure the text that will be displayed inside the LI element.
Using `` and ${} constructions of ES6 as in my example its quite easy, but you can as well use the example below if you find it easier. I may argue it's indeed easier on the eyes, even though I prefer ES6 way.
let textnode = document.createTextNode(staff + ", " + desc);
Finally, append the text node, then append the li element. Done.
I have a string containing html elements, now I need to select some elements and remove them from the string.
In JQuery I tried the following:
html_string = "<ul><li data-delete>A<li><li>B</li></ul>";
html_clean_string = $(html_string).remove('[data-delete]').html();
This is what I expected:
"<ul><li>B</li></ul>"
But I got the same original string. So how can I use CSS selectors to remove html elements from a string?
You can do it like this:
var html_string = "<ul><li data-delete>A</li><li>B</li></ul>";
var elems = $(html_string);
elems.find('[data-delete]').remove();
var html_clean_string = elems[0].outerHTML;
You had a couple of issues:
.remove() only operates on the elements in the jQuery object, not on child object so you have to .find() the appropriate child elements before you can remove them.
Since you want the host top level HTML too, you will need the .outerHTML.
You had mistakes in your html_string.
Working jsFiddle: http://jsfiddle.net/jfriend00/x8ra6efz/
You can also save a little jQuery with more chaining like this:
var html_string = "<ul><li data-delete>A</li><li>B</li></ul>";
var html_clean_string = $(html_string).find('[data-delete]').remove().end()[0].outerHTML;
Working jsFiddle:http://jsfiddle.net/jfriend00/wmtascxf/
Let's say I have two <div> elements in the DOM. I can easily switch their markup between them with jQuery, like so:
function switch_html(el1, el2){
var el1_content = el1.html();
var el2_content = el2.html();
el1.html(el2_content);
el2.html(el1_content);
}
However, how do I actually switch the DOM elements, and not just copy & switch the HTML source? For example, in the app I am currently developing, I am swapping out <div> contents that include filled out <input> fields. If the source HTML of these fields is merely copied, then the values contained within the inputs will be lost.
You can move the actual DOM nodes using jQuery append / element.appendChild. Move the actual DOM nodes, don't try to make copies/clones.
Try this:
var children1 = $('#el1').children();
var children2 = $('#el2').children();
$('#el1').append(children2);
$('#el2').append(children1);
javascript has a cloneNode() function which performs a "deep" clone when true is passed.
So:
function switch_html(el1, el2){
var el1Clone = el1.cloneNode(true);
var el2Clone = el2.cloneNode(true);
el2.replaceNode(el1Clone);
el1.replaceNode(el2Clone);
}
Or for a (slightly) more performant solution:
function switch_html(el1, el2){
var el2Clone = el2.cloneNode(true);
el2.replaceNode(el1);
el1.replaceNode(el2Clone);
}
Please note, that the assumption here is you want to do this in raw JavaScript, ie: no jQuery
I swear this was just working fine a few days ago...
elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
while (elm[0]){
frag.appendChild(elm[0]);
}
Right, so, this should append each node from our elm node list. When the first one is appended, the second "moves" to the first position in node list, hence the next one is always elm[0]. It should stop when the elm nodeList is completely appended. However, this is giving me an infinite loop. Thoughts?
EDIT - because I've gotten the same answer several times...
A nodeList is not an array, it is a live reference. When a node is "moved" (here, appended) it should be removed automatically from the node list. The answers all saying "you're appending the same element over and over" - this is what's happening, it shouldn't be. A for loop shouldn't work, because when the first node is appended, the next node takes its index.
2nd EDIT
So the question is now "why is the nodeList behaving as an array?". The node list SHOULD be updating every time a node is being appended somewhere. Most peculiar.
Solution (in case someone needs something to handle live + non-live node lists)
elm = (/*however you're getting a node list*/);
var frag = document.createDocumentFragment();
var elength = elm.length;
for (var b = 0; b<elength; b++){
if (elm.length === elength){
frag.appendChild(elm[b]);
} else {
frag.appendChild(elm[0].cloneNode());
}
}
Basically, just checking to see if the node list has changed length.
From the MDN Docs
Element.querySelectorAll
Summary
Returns a non-live NodeList of all elements descended from the element on which it is invoked that match the specified group of CSS selectors.
Syntax
elementList = baseElement.querySelectorAll(selectors);
where
elementList is a non-live list of element objects.
baseElement is an element object.
selectors is a group of selectors to match on.
From the docs above you can see it does not automatically remove it when you append it to another element since it is non live. Run a demo to show that feature.
var selector = "div";
elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
console.log("before",elm.length);
frag.appendChild(elm[0]);
console.log("after",elm.length);
When the code above runs, in the console you get.
before 3
after 3
If you want to do the while loop, convert to an array and shift() the items off
var selector = "div";
var elmNodeLIst = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
var elems = Array.prototype.slice.call(elmNodeLIst );
while (elems.length) {
frag.appendChild(elems.shift());
}
console.log(frag);
You are appending the first item in the node list, over and over and over. You never removing any items from the array, but always adding the first one to the fragment. And the first one is always the same.
elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
while (elm.length){
frag.appendChild(elm.shift());
}
This may be closer to what you meant to do. We can use while (elm.length) because as items get removed form the array, eventually length will be zero which is a flasy value and the loop will stop.
And we use elm.shift() to fetch the item from the array because that method will return the item at index zero and remove it from the array, which gives us the mutation of the original array we need.
I think you thought this might work because a node can only have one parent. Meaning adding somewhere removes it from the previous parent. However, elm is not a DOM fragment. It's just a aray (or perhaps a NodeList) that holds references to element. The array is not the parent node of these elements, it just holds references.
Your loop might work if you had it like this, since you are query the parent node each time for its children, a list of node that will actually change as you move around:
elm = document.getElementById(id);
var frag = document.createDocumentFragment();
while (elm.children[0]){
frag.appendChild(elm.children[0]);
}
I wouldn't have expected it to work in the first place.
Your elm array is initialized, and never updated. Even if the result from running document.querySelectorAll(selector); would return something different, this doesn't change your current references in the array.
You would either need to rerun the selector, or manually remove the first element in the array after appending it.
elm[0] is static and unchanging in above code
fix is as below
elm = document.querySelectorAll(".container");
var frag = document.createDocumentFragment();
console.log(elm);
var i=0;
while (elm[i]){
frag.appendChild(elm[i++]);
}
I didn't actually focus much on the code (and if it made sense -judging from the comments- or not); but if this worked a few days ago then the problem is in the input you are giving to your code selector.
That's when Unit Testing comes in handy. If you can remember the input with which the code worked, then you can make it work again and start debugging from there.
Otherwise, you are just lying to yourself.
It's an infinite loop as it's written right now because elm[0] always refers to the same element, and that element is never null (any non-null/non-zero result would be true). You also don't do anything with the elements themselves to make it iterate across the list. You should be using a for loop instead of a while or at least having some kind of indexer to try to traverse the collection.
elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
for (i= 0; i < elm.length; i++)
{
frag.appendChild(elm[i]);
}
Edit:
From the documentation:
A "live" collection
In most cases, the NodeList is a live collection. This means that changes on the DOM tree >are going to be reflected on the collection.
var links = document.getElementsByTagName('a'); // links.length === 2 for instance
document.body.appendChild( links[0].cloneNode(true) ); // another link is added to the document
// the 'links' NodeList is automatically updated
// links.length === 3 now. If the NodeList is the return value of document.querySelectorAll, it is NOT live.
Going on this documentation, your current usage of the method indicates you do not have a live NodeList. Thus appending will never modify the original list. You will either need to modify your usage within the loop to mirror this usage of .cloneNode(true) or iterate manually.
I would like to find all occurrence of the $ character in the dom, how is this done?
You can't do something semantic like wrap $4.00 in a span element?
<span class="money">$4.00</span>
Then you would find elements belonging to class 'money' and manipulate them very easily. You could take it a step further...
<span class="money">$<span class="number">4.00</span></span>
I don't like being a jQuery plugger... but if you did that, jQuery would probably be the way to go.
One way to do it, though probably not the best, is to walk the DOM to find all the text nodes. Something like this might suffice:
var elements = document.getElementsByTagName("*");
var i, j, nodes;
for (i = 0; i < elements.length; i++) {
nodes = elements[i].childNodes;
for (j = 0; j < nodes.length; j++) {
if (nodes[j].nodeType !== 3) { // Node.TEXT_NODE
continue;
}
// regexp search or similar here
}
}
although, this would only work if the $ character was always in the same text node as the amount following it.
You could just use a Regular Expression search on the innerHTML of the body tag:
For instance - on this page:
var body = document.getElementsByTagName('body')[0];
var dollars = body.innerHTML.match(/\$[0-9]+\.?[0-9]*/g)
Results (at the time of my posting):
["$4.00", "$4.00", "$4.00"]
The easiest way to do this if you just need a bunch of strings and don't need a reference to the nodes containing $ would be to use a regular expression on the body's text content. Be aware that innerText and textContent aren't exactly the same. The main difference that could affect things here is that textContent contains the contents of <script> elements whereas innerText does not. If this matters, I'd suggest traversing the DOM instead.
var b = document.body, bodyText = b.textContent || b.innerText || "";
var matches = bodyText.match(/\$[\d.]*/g);
I'd like to add my 2 cents for prototype. Prototype has some very simple DOM traversal functions that might get exactly what you are looking for.
edit so here's a better answer
the decendants() function collects all of the children, and their children and allows them to be enumerated upon using the each() function
$('body').descendants().each(function(item){
if(item.innerHTML.match(/\$/))
{
// Do Fun scripts
}
});
or if you want to start from document
Element.descendants(document).each(function(item){
if(item.innerHTML.match(/\$/))
{
// Do Fun scripts
}
});