Adding and removing a division using JavaScript - javascript

<script type="text/javascript">
var num=2;
function addElement()
{
var ni = document.getElementById('myDiv');
var newdiv = document.createElement('div');
var divIdName = 'my'+num+'Div';
newdiv.setAttribute('id',divIdName);
newdiv.innerHTML = 'Subject-' + num + '* :<input type="text" id=textbox"' + num + '"/><a href=\'#\' onclick=\'removeElement('+divIdName+')\'>Remove</a>';
ni.appendChild(newdiv);
num=num+1;
}
function removeElement(divNum)
{
alert(divNum.id);
var d = document.getElementById('myDiv');
var dd =document.getElementById(divNum.id);
d.removeChild(dd);
for(var i=0;i<d.childNodes.length;i++)
{
if(d.childNodes[i].id==divNum.id)
{
d.removeChild(d.childNodes[i]);
}
}
}
</script>
It is working fine in Internet Explorer but in Firefox it is giving error like the element my1Div is not defined. Why is this happening and how can it be corrected?
Thanks.

Change
newdiv.innerHTML = 'Subject-' + num + '* :<input type="text" id=textbox"' + num + '"/><a href=\'#\' onclick=\'removeElement('+divIdName+')\'>Remove</a>';
to
newdiv.innerHTML = 'Subject-' + num + '* :<input type="text" id=textbox"' + num + '"/><a href=\'#\' onclick=\'removeElement("'+divIdName+'")\'>Remove</a>';
(add the " quotes)
and then change
var dd =document.getElementById(divNum.id);
to
var dd =document.getElementById(divNum);
(remove .id)

Your quotes are wrong in both the removeElement call and the textbox ID. This always happens when kludging HTML together from strings, especially JavaScript inside HTML inside JavaScript strings. That's too many levels of nesting for the mind to cope with. Use DOM methods instead.
Avoid setAttribute, it's less readable than normal DOM Level 1 HTML methods, and has many bugs in IE. removeElement is also a very odd way of saying d.parentNode.removeChild(d) twice — ineffectively, unless you have two elements with the same ID. (Which you shouldn't as it's quite invalid. If you did, it would fail for every other matching child as you are doing a destructive forward iteration.)
If you use a closure you could also lose all the nasty stuff with remembering which element is which, and replace the lot with:
function addElement() {
var newdiv= document.createElement('div');
newdiv.appendChild(document.createTextNode('Subject-' + num + '* :'));
newdiv.appendChild(document.createElement('input'));
newdiv.appendChild(document.createElement('a'));
newdiv.lastChild.href='#';
newdiv.lastChild.onclick= function() {
newdiv.parentNode.removeChild(newdiv);
return false;
};
newdiv.lastChild.appendChild(document.createTextNode('Remove'));
document.getElementById('myDiv').appendChild(newdiv);
}

I haven't ran the code but I guess that you might be hitting DOM TextNodes, which don't have many of the methods that elements have defined. To work around this you can specifically check for text nodes using something like
if (d.childNodes[i].nodeName === "#text") continue;
Of course this might be the totally wrong solution. If you could edit your post and say which line is erroring that might help.

Related

Jquery - find all elements where the start element matches closest parent with that selector

I have HTML structures in a form system where a html node will have data-src="formname" and this will contain lots of html nodes with data-fld="fieldname". This would be easy to parse but sometimes a field can be a host of a subform that itself contains data-src and data-fld.
When I search for [data-src='name'] using jquery find selectors I get both the immediate data-fld elements and the ones contained in a child data-src, I only want the former, not the latter.
I've created a fiddle to demonstrate:
<div data-src="mainform">
<div data-fld="field1">fld1</div>
<div data-fld="field2">
<div data-src="subform">
<div data-fld="subfield1">subfld1</div>
</div>
</div>
</div>
<div id="info"></div>
And the JS:
var result = "";
var find = "mainform";
var src = $("[data-src='" + find + "']");
src.find("[data-fld]").each(function() {
var ele = $(this);
if (ele.closest("[data-src='" + find + "']") === src) {
result += "Child field : " + $(this).data("fld") + " ";
}
});
$("#info").text(result);
The above code works, by virtue of that IF statement, I think it would be nice to be able to select "[data-fld]" where its closest "[data-src]" is the one I'm working on, and I wondered if (a) there's an inherent JQuery/CSS selector way of doing this or (b) is there otherwise a better solution to this code.
Not only because we want elegant code but also because asking for closest on every loop iteration is going to be a performance issue, possibly.
using immediate children selector
var result = "";
var find = "mainform";
var src = $("[data-src='" + find + "']");
src.find("[data-fld]").first().each(function() {
var ele = $(this);
result += "Child field : " + $(this).data("fld") + " ";
});
$("#info").text(result);

Write text over div without using .text()

I've got again a rather simple question, that I couldn't find an answer to.
I was using sofar the Jquery function .text() to write text on mouseenter on a dynamically created div. I came to realise that this only worked on my Iceweasel, but not in Chrome for instance. Instead ot .text() everywhere people advised of using the .val(), but I can't seem to figure out exactly how to use it in my implementation, since the divs had no previous text value.
Please find below a simple code, with .text() to understnad the question.
(function(){
for (var i = 0; i < 3; i++) {
var span = document.createElement("span");
span.innerHTML = "<img width=\"" + data.size[i][0] + "\" height=\"" + data.size[i][1] + "\" id=\"" + i + "\">";
span.style.position = "absolute";
span.style.left = data.coords[i][0] + "px";
span.style.top = data.coords[i][1] + "px";
document.body.appendChild(span);
}
}());
for (var i=0; i<3; i++) {
$('#' + i).mouseenter(function() {
$(this).text("text");
});
$('#' + i).mouseleave(function() {
$(this).text("")
});
}
http://jsfiddle.net/ckpx6esj/1/
I hope someone can give me an idea, of how to apply .val() or use something else entirely to make this work for chrome also.
Best Regards and Thanks in advance!
The problem is that you put text in an image tag!
<img>Some text</img>
This is invalid HTML, see this answer.
If you want text over an image, I suggest using a div with background: url(...) instead.
Updated fiddle.
The cleverest I could think to don't screw up your for loop is appending a <p> tag containing your text and removing it on mouseleave:
for (var i=0; i<3; i++){
$('#' + i).on("mouseenter",function() {
$(this).parent().append("<p>text</p>");
});
$('#' + i).on("mouseleave",function() {
$(this).parent().find("p").remove();
});
}
Fiddle:
http://jsfiddle.net/ckpx6esj/2/
Besides, text was not working because you are listening to the image (<img>) instead of the span. Images has no .text() prototype, hence you should access its parent() (which is a <span> in that case) if you want to use the .text() prototype, but using .text() on the parent will remove the image, hence the idea of appending the text and removing it later.
According to specification, val() function is to set value attribute and it only matters for input fields on your page. text() function is to change content of your element.
The .val() method is primarily used to get the values of form elements
such as input, select and textarea.
So you should use text() function in your code.
Also according to your code you change text property of <img> element. This is not good. You should change text of your <span>. So just move your id to span element.
If you want the jQuery equivalent of Javascript's native innerHtml, go for $(this).html('text');.
Take a look at these functions:
http://api.jquery.com/html/
$(this).html('text');
http://api.jquery.com/append/
$(this).append('text'); // Note that this appends instead of replaces
http://api.jquery.com/val/
$(this).val('text');
Or if you're feeling adventurous:
http://api.jquery.com/appendto/
$('text').appendTo($(this)); // Performance penalty for creating an object out of 'text'
First I will use class instead id, it will save using the second loop,
also if you want to have also text and also image you can do it but it will be littel complicated I would recommand add some child element to the span that will contain the text, I didnt do it just for the challenge
http://jsfiddle.net/ckpx6esj/5/
simple plugin to change the text without changing the html elements
$.fn.selectorText = function(text) {
var str = '';
this.contents().each(function() {
if (this.nodeType === 3) {
if(typeof(text) === 'string'){
this.textContent = text;
return false;
}else{
str += this.textContent || this.innerText || '';
}
}
});
return str;
};
var thisData = [{
'coords' : [[100,100], [300, 300], [200, 200]],
'size' : [[30, 30], [30, 30], [30, 30]]
}];
var data = thisData[0];
(function(){
for (var i = 0; i < 3; i ++){
var span = document.createElement("span");
span.setAttribute('class','spanImage');
span.style.position = "absolute";
span.style.left = data.coords[i][0] + "px";
span.style.top = data.coords[i][1] + "px";
span.innerHTML = "\n<img width=\"" + data.size[i][0] + "\" height=\"" + data.size[i][1] + "\" id=\"" + i + "\">";
document.body.appendChild(span);
}
$('.spanImage')
.on( 'mouseenter', function() {
$(this).selectorText('text');
})
.on( 'mouseleave', function() {
$(this).selectorText('');
});
}());

How to get the value from multiple unique ids

I've been working on a form were i can make extra inputfields and selectfields thanks to this site! Al those fields get unique id (3011, 3012, 3013 to a max of 3019) and (rc1, rc2 etc.)
with a removal link.
var i = 2;
var limit = 10;
function createInput () {
if (i == limit) {
alert("You have reached the limit of adding " + i + " inputs");
}
else
var field_area = document.getElementById('301X')
var div = document.createElement("div");
div.id = 'SA '+i;
var input = document.createElement("input");
div.innerHTML = '301'+i ;
input.id = '301'+i;
input.name = '3011';
input.type = "text"; //Type of field - can be any valid input type like text,file,checkbox etc.
input.value = "";
input.size = '80';
var rcnode = document.getElementById("rc1") // bepaal var van de select menu id relcode
var rcclone = rcnode.cloneNode(true); // bepaal var van de clone van de select
rcclone.id = 'rc'+i; // het nummeren van id clone
div.appendChild(input);
div.appendChild(rcclone);
field_area.appendChild(div);
and it works fine (would not surprise me that it can be beter) Now I want to catch the values. I succeed to cath it from one value with this script:
var x = 1;
function catchInput () {
var said = '301'+x;
var rcid = 'rc'+x;
var Valsaid = document.getElementById(said).value;
var Valrcid = document.getElementById(rcid).value;
alert (said + ' ' + Valsaid + Valrcid);
}
but I want to catch them all. I have been playing around with arrays and the for-loop but I can't figure it out. Any help, point in the right direction much appreciated.
Herman
There are several possibilities :
1) prefix your id's with a common string, then use jQuery (for example) to find all elements with id's starting with this prefix : $('[id^=yourPrefix]')
2) if you want to stick with POJS, "group" these dynamically generated elements by assigning a common class to them. you can then use getElementsByClassName() , which is not available in all browsers though (actually only a problem with IE <= 8)
3) still if you want to stick with POJS but need extended browser support, you can prefix your id's with a common string, then loop on your elements (maybe restricting the set using getElementsByTagName()) and test if the id string starts with this prefix using indexOf() , for example (that's most probably how jQuery implements this functionality)
Hope this helps...

Javascript addEventListener inside a loop

I'm trying to add an event listener to some elements which I generate in the loop. I must use div.lastChild - although in this example it's pretty stupid. But that's just demonstration:
<div id="someArea">
</div>
<script type="text/javascript">
var func = function() {
alert('Callback works');
}
var func1 = function() {
alert('Test');
}
var func2 = function() {
alert('Test2');
}
var div = document.getElementById("someArea");
var callbacks = [func, func1, func2];
for(var i = 0; i <= 2; i++) {
div.innerHTML += '<input type="button" value="' + i + '" />';
(function(i) {
console.log(div.lastChild);
div.lastChild.addEventListener('click', callbacks[i], false);
}(i));
}
</script>
The event works only for the last button. What am I missing here?
Why it's not working.
When you do this...
div.innerHTML += '<input type="button" value="' + i + '" />';
...in every iteration of the loop you're destroying the old DOM nodes inside div (and therefore their handlers), and recreating new nodes (but no handlers). The destruction includes the input elements that were added in previous iterations.
That's why only the last one works, since after that point, you've assigned the handler to the last element, but the other ones are brand new and untouched.
A solution.
Instead of treating the DOM as though it was a string of HTML markup, consider using DOM methods for element creation...
for(var i = 0; i <= 2; i++) {
var input = document.createElement('input');
input.type = 'button';
input.value = i;
input.addEventListener('click', callbacks[i], false);
div.appendChild(input);
}
Notice that I removed the immediately invoked function. For your code, it was unnecessary since i is being evaluated in the loop instead of later in the handler.
The DOM is not HTML, but .innerHTML makes you think it is.
It's important to understand innerHTML. When working with the DOM, there is no HTML markup. So when you get the .innerHTML, the DOM is being analyzed, and a new HTML string is created.
When you assign to .innerHTML, you're destroying all the current content, and replacing it with new nodes created from the HTML string.
So when you do...
div.innerHTML += '<input...>'
...you're first creating the new HTML string from the current content, then concatenating the new HTML content to the string, then destroying the old nodes and creating new ones from the new string.
This is terribly inefficient, and as you've already seen, it destroys any data associated with the original elements, including handlers.

JavaScript RegExp match text ignoring HTML

Is it possible to match "the dog is really really fat" in "The <strong>dog</strong> is really <em>really</em> fat!" and add "<span class="highlight">WHAT WAS MATCHED</span>" around it?
I don't mean this specifically, but generally be able to search text ignoring HTML, keeping it in the end result, and just add the span above around it all?
EDIT:
Considering the HTML tag overlapping problem, would it be possible to match a phrase and just add the span around each of the matched words? The problem here is that I don't want the word "dog" matched when it's not in the searched context, in this case, "the dog is really really fat."
Update:
Here is a working fiddle that does what you want. However, you will need to update the htmlTagRegEx to handle matching on any HTML tag, as this just performs a simple match and will not handle all the cases.
http://jsfiddle.net/briguy37/JyL4J/
Also, below is the code. Basically, it takes out the html elements one by one, then does a replace in the text to add the highlight span around the matched selection, and then pushes back in the html elements one by one. It's ugly, but it's the easiest way I could think of to get it to work...
function highlightInElement(elementId, text){
var elementHtml = document.getElementById(elementId).innerHTML;
var tags = [];
var tagLocations= [];
var htmlTagRegEx = /<{1}\/{0,1}\w+>{1}/;
//Strip the tags from the elementHtml and keep track of them
var htmlTag;
while(htmlTag = elementHtml.match(htmlTagRegEx)){
tagLocations[tagLocations.length] = elementHtml.search(htmlTagRegEx);
tags[tags.length] = htmlTag;
elementHtml = elementHtml.replace(htmlTag, '');
}
//Search for the text in the stripped html
var textLocation = elementHtml.search(text);
if(textLocation){
//Add the highlight
var highlightHTMLStart = '<span class="highlight">';
var highlightHTMLEnd = '</span>';
elementHtml = elementHtml.replace(text, highlightHTMLStart + text + highlightHTMLEnd);
//plug back in the HTML tags
var textEndLocation = textLocation + text.length;
for(i=tagLocations.length-1; i>=0; i--){
var location = tagLocations[i];
if(location > textEndLocation){
location += highlightHTMLStart.length + highlightHTMLEnd.length;
} else if(location > textLocation){
location += highlightHTMLStart.length;
}
elementHtml = elementHtml.substring(0,location) + tags[i] + elementHtml.substring(location);
}
}
//Update the innerHTML of the element
document.getElementById(elementId).innerHTML = elementHtml;
}
Naah... just use the good old RegExp ;)
var htmlString = "The <strong>dog</strong> is really <em>really</em> fat!";
var regexp = /<\/?\w+((\s+\w+(\s*=\s*(?:\".*?"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/gi;
var result = '<span class="highlight">' + htmlString.replace(regexp, '') + '</span>';
A simpler way with JQuery would be.
originalHtml = $("#div").html();
newHtml = originalHtml.replace(new RegExp(keyword + "(?![^<>]*>)", "g"), function(e){
return "<span class='highlight'>" + e + "</span>";
});
$("#div").html(newHtml);
This works just fine for me.
Here is a working regex example to exclude matches inside html tags as well as javascripts:
http://refiddle.com/lwy6
Use this regex in a replace() script.
/(a)(?!([^<])*?>)(?!<script[^>]*?>)(?![^<]*?<\/script>|$)/gi
this.keywords.forEach(keyword => {
el.innerHTML = el.innerHTML.replace(
RegExp(keyword + '(?![^<>]*>)', 'ig'),
matched => `<span class=highlight>${matched}</span>`
)
})
You can use string replace with this expression </?\w*> and you'll get your string
If you use jQuery, you can use the text property on the element containing the text you're searching for. Given this markup:
<p id="the-text">
The <strong>dog</strong> is really <em>really</em> fat!
</p>
This would yield "The dog is really really fat!":
$('#the-text').text();
You could do your regex search on that text instead of trying to do so in the markup.
Without jQuery, I'm unsure of an easy way to extract and concatenate the text nodes from all child elements.

Categories