Why am I returned undefined? - javascript

I am wanting to get an attribute of the parent element that my cursor is inside of.
I have been totally unsuccessful for about two hours now, so I'm posting to see if anybody has any suggestions. This is the function I have but don't know why parentID only returns undefined:
function getAttrOfParent() {
var newRange = rangy.getSelection().getRangeAt(0);
var parentElement = newRange.commonAncestorContainer;
var parentID = $(parentElement).attr('id');
alert(parentID);
}
This works fine to get the text of the parent element...
function getTextOfParent() {
var newRange = rangy.getSelection().getRangeAt(0);
var parentElement = newRange.commonAncestorContainer;
var parentText = $(parentElement).text();
alert(parentText);
}
...and this works fine to get the title of a specified element.
function getAttrOfElement() {
var parentID = $('#1').attr('id');
alert(parentID);
}
Here's my jsFiddle, you have to click inside of the text area for the first two functions to work.

parentElement = newRange.commonAncestorContainer isn't giving you the parent element, it is giving you the parent node.
This is fairly obvious if you console.log(parentElement) to see what is being selected.
If you select text entirely inside one of your spans (which have ids) then you get the text node (which doesn't have an id).
If you select more text, you get the paragraph element node (which doesn't have an id).
You might want to try something like var parentElement = $(newRange.commonAncestorContainer).parents('span');

Related

Changing the background color of text on a webpage in JavaScript (when element ID or name isn't available)

What I want to achieve is that once a web page is loaded, find some text in it and highlight the text. My code what I've written till now is as follows.
First I match the textContent using a regular expression
(Why am I using this approach is because I don't have the ID or name for the div where the textContent is present, thus, I cant go for getElementsByName() or getElementById()). To check if the text exists, I first compare it's length and then proceed to highlight it.
The text I find is at index 0 of the text variable. I cant access backgroundColor() method that is why it's been commented out for now. I could access fontcolor() but even it didn't seem to work.
I am new to JS so some direction would be greatly appreciated.
I would like to know why fontcolor() doesn't work even though I can access it.
What would be the best approach to solve this as I cant access backgroundColor().
(function highlightText(){
let text = document.body.textContent.match(/abcxyz/);
if (text[0].length == 6)
{
text[0].fontcolor('yellow');
//text[0].backgroundColor('yellow');
console.log(text[0]); //prints abcxyz
return text[0];
}
return null;
})()
SOLUTION:
The approach based off on the solution provided by #forlen:
What I did was loop through all the nodes, if the textContent matches the regular expression then push them into an array and as I knew what I wanted was the root element so I got the last element of the array and changed its backgroundColor.
(function highlightText() {
let allNodes = document.body.getElementsByTagName("*");
var arr= [];
for (let node of allNodes) {
if (node.textContent.match(/abcxyz/)) {
arr.push(node);
}
}
text = arr[(arr.length)-2];
text.style.backgroundColor = "yellow";
})();
I recommend looping through all nodes in body and changing style like this:
(function highlightText() {
let allNodes = document.body.getElementsByTagName("*");
for (let node of allNodes) {
if (node.textContent === "abcxyz") {
node.style.color = "yellow";
}
}
})();

parentNode.removeChild not removing last element. But why?

I am working on a javascript code where I can clone an element, but also want to delete one on click. Cloning works, but I can't remove one.
I have the following elements.
<input list="accountsdeb" name="deblist[1]" id="deblist" autocomplete="off">
<input list="accountsdeb" name="deblist[2]" id="deblist" autocomplete="off">
And now I want to remove the last one on click (last = highest number).
function remove1() {
var dbl = document.querySelectorAll('#deblist').length;
var dbla = dbl;
var elem = document.getElementsByName("deblist["+dbla+"]");
alert(dbla);
//var last = debelelast - 1;
elem.parentNode.removeChild(elem);
}
As an orientation I used to have a look on an example from W3S and Stack. I have also seen that this works:
var elem = document.getElementById("myDiv");
elem.parentNode.removeChild(elem);
But this is random and as you can see I have tried to include this in my code.
The error I get is:
Uncaught TypeError: Cannot read property 'removeChild' of undefined
at HTMLAnchorElement.remove1 (index.php:179)
Where's the problem in my code, where is my thinking wrong?
I see two issues in the piece of code you provided,
deblist is used as id for 2 elements which is not advisable and due to this document.querySelectorAll('#deblist').length returns 2 (I am not sure if you intending to do so)
document.getElementsByName() (check here) will return a NodeList which needs to be iterated in order to access any of the returned elements. So here you need to select the child element by giving its index. In your case elem will have one element for the matched name deblist[2] and hence you need to access it like elem[0] for selecting its parent and deleting its child.
So the updated the code would be,
var dbl = document.querySelectorAll('#deblist').length;
var dbla = dbl;
// console.log('dbla', dbla);
var elem = document.getElementsByName("deblist["+dbla+"]");
// console.log('elem 0', elem[0]);
// console.log('elem parentNode', elem[0].parentNode);
//var last = debelelast - 1;
elem[0].parentNode.removeChild(elem[0]);
Check the fiddle here
If the inputs are part of a group they could share a name property or such, and the use of jQuery could help you do something like...
$("input[name='group1']").last().parent().remove()
Or if not part of a group then just....
$("input").last().parent().remove()

Javascript: Add same <li> to mulitple <ul> [duplicate]

I just notice I couldn't do
var d1 = document.createElement('div');
var d2 = document.createElement('div');
var p = document.createElement('p');
d1.appendChild(p); // d1 has p now
d2.appendChild(p); // d2 has p now
// but where is p in d1 ?
Some would say it's logic, but well, when I first noticed that I thought how uncool it was.
Why isn't that possible ?
The DOM is a tree structure.
When you append an element, you change its parent.
A node, in the browser, is much more than just the text inside your P (that string could be shared, in fact). It also has a position, dimensions, a visibility, receives events that could have been fired in child elements, propagate events to its parent, and so on. Everything here depends on the position in the tree. Just like would many CSS selectors. It doesn't make a lot of sense to imagine it's the same element at two places, it's better to think about it as two nodes, with maybe some identical content.
If you want to have the same content at two places, you have to clone it.
jQuery's appendTo() method inserts "every element in the set of matched elements to the end of the target". Try this:
p.appendTo(".div-class1, .div-class2")
for AppendChild same element multiple times , we can use this way :
//main function
function appendChildMultiple(parent) {
//check function argument is an element
if (parent.nodeType !== undefined) {
const pTag = document.createElement("p");
pTag.innerText = "This is the appended element";
//finally append child to parent
parent.appendChild(pTag);
}
}
and :
// target the wrapper and create test elements
const wrapper = document.querySelector(".wrapper");
const d1 = document.createElement("div");
const d2 = document.createElement("div");
//append test elements to wrapper
wrapper.appendChild(d1);
wrapper.appendChild(d2);
//use appendChildMultiple function
appendChildMultiple(d1);
appendChildMultiple(d2);
//we appended "pTag" multiple times
if we use Functions , we can AppendChild same element multiple times whitout cloneNode
https://codepen.io/kiumad/pen/eYMNKYa

Piping createElement and appendChild in JavaScript

Can someone please explain to me, why
var Node = document.createElement("testing");
var Parent = document.createElement("testingOne")
Parent.appendChild(document.createElement("hi"));
Node.appendChild(Parent);
produces a different result from
var Node = document.createElement("testing");
var Parent = document.createElement("testingOne")
.appendChild(document.createElement("hi"));
Node.appendChild(Parent);
In the second snippet the element testingOne doesn't even get included. Why does the piping do this?
Your first example will result in
<testing><testingone><hi></hi></testingone></testing>
Parent will contain the testingOne and the hi element will be appended to it.
While the second example will result in
<testing><hi></hi></testing>
Because Parent will contain the hi element, which is returned by the appendChild method.

How do I get element's className inside loop of elements?

I am trying to create a function that given a divid, and a list of classes, will then do some text replacing inside them.
Having learned of how Firefox Dom is handling text nodes differently, I read that I had to use javascript to loop through the elements, sibling to nextSibling.
The last obstacle I had in my script, of which you see a small portion of, is getting the classname. I need the class name so that I can filter down what content get's text replaced.
Having looked all the answers, and with the help of a co-worker named Ryan at work, we have redone this in jquery.
$(divid).find(".status_bar").each( function() {
var value = $.trim($(this).text());
// if value is not defined thru browser bugs do not replace
if (typeof(value) != 'undefined') {
// it is a text node. do magic.
for (var x = en_count; x > 0; x--) {
// get current english phrase
var from = en_lang[x];
// get current other language phrase
var to = other_lang[x];
if (value == from) {
console.log('Current Value ['+value+'] English ['+from+'] Translation ['+to+']');
value = to;
$(this).attr('value', to);
}
}
}
});
This currently works in all areas, except in the replacing of text.
The reason I had originally with doing this in jQuery, had to be not sure I could loop thru elements, and avoid the problem with firefox and text nodes.
I am doing a loop of all elements inside a div, and I now need to get the classname of the element that I am looping by.
Then i can check if the current element's class is one, I need to do something with...
// var children = parent.childNodes, child;
var parentNode = divid;
// start loop thru child nodes
for(var node=parentNode.firstChild;node!=null;node=node.nextSibling){
var myclass = (node.className ? node.className.baseVal : node.getAttribute('class'));
}
But this code for getting the classname only get's null values.
Any suggestions?
For those of you who are trying to figure out what the whole point is, read this JavaScript NextSibling Firefox Bug Fix I have code that does my language translation that works in Google Chrome and IE. But when I use it in Firefox, and try to translate div content after ajax has loaded it, it fails because of the whitespace issue.
I really don't have a preference of jQuery or Pure Javascript, I just want a working solution.
Thank you all for being patient. I personally thought I was extremely clear in my description, I apologize if it wasn't. I wasn't trying to be obscure or make it difficult to get help. But please don't insult me, by implying I am trying to make it unclear.
Thanks.
Hm... You have jQuery but don't use it?
$(divid).children(".yourSpecialClassName").each( function() {
doSomethingWith(this);
});
To get the CSS class attribute value, this will do:
$(divid).children().each( function() {
alert(this.className);
});
Based on the function you posted now, you want this:
$(divid).find(".status_bar").each( function() {
$(this).text( function(i, text) {
var x = $.inArray(en_lang, $.trim(text));
if (x > -1) {
console.log('Current Value ['+text+'] English ['+en_lang[x]+'] Translation ['+other_lang[x]+']');
return other_lang[x];
}
return text;
});
});
And please, don't ever use "do magic" as a comment again. This is incredibly lame.
EDIT. This can be made much more efficient (superfluous console.log() removed):
$(divid).find(".status_bar").each( function() {
// prepare dictionary en_lang => other_lang
var dict = {};
$.each(en_lang, function(x, word) { dict[word] = other_lang[x]; });
$(this).text( function(i, text) {
var t = $.trim(text);
return (t in dict) ? dict[t] : text;
});
});
if you are using jquery you can do this:
$("#myDiv").find("*").each(
function(){
var myclass = $(this).attr("class");
}
);
Your sample code doesn't make sense.
$(this).attr('value', to);
'value' is an attribute of the tag, not the text content.
Did you really mean to do this instead?
$(this).text(to);
Also, you've re-edited your question, but you're still trying to loop through the child nodes using non-jQuery methods. You said "The last obstacle I had in my script, of which you see a small portion of, is getting the classname. I need the class name so that I can filter down what content get's text replaced."
If you are using jQuery it is completely unnecessary to loop through anything to get a class name. You simply have to use a proper selector in the first place.
$(divid).find(".status_bar.replaceme").each( function() {
// .replaceme is whatever class you're using for the stuff you want to change
// .status_bar.replaceme matches all elements with BOTH status_bar and replaceme classes
var value = $.trim($(this).text());
// if value is not defined thru browser bugs do not replace
if (typeof(value) != 'undefined') {
// it is a text node. do magic.
// NOTE: The following is inefficient but I won't fix it.
// You're better off using an associative array
for (var x = en_count; x > 0; x--) {
// get current english phrase
var from = en_lang[x];
// get current other language phrase
var to = other_lang[x];
if (value == from) {
console.log('Current Value ['+value+'] English ['+from+'] Translation ['+to+']');
// value = to; <-- useless, get rid of it.
$(this).text(to);
// or $(this).html(to);
}
}
}
});

Categories