Why does innerHTML not change src of an image? - javascript

I have to set one src to an image object. Then I change it.
But if I add something to the element (content of element), such as
meaning.innerHTML += ")";
(where meaning is parent element of image), then if change the src of object it won't affect the document.
Example: http://jsfiddle.net/WcnCB/3/
Could you explain me why it happens, and how to fix it?

meaning.innerHTML += ')'; does more than you think. Visually it just appends a ) character, but behind the scenes what happens is:
meaning.innerHTML = meaning.innerHTML + ')';
So, you're first converting the DOM to a string representation (HTML), then adding a ) character, and finally have convert it back from HTML to the DOM. All elements the HTML represents are created again, and meaning is replaced by those new elements. So your old one is distroyed.
The simplest solution is to use createTextNode: http://jsfiddle.net/WcnCB/4/.
meaning.appendChild(document.createTextNode(")"));

By writing innerHTML += ... you are overwriting the previous HTML and destroying every reference to it - including the actual_button variable.
Why are you using innerHTML += ... anyway? You should be doing:
meaning.appendChild(document.createTextNode("(Something"));

When you do the greatest sin of all, that is .innerHTML += (specifically innerHTML combined with +=, neither of them are bad alone), what happens is:
Serialize the element's DOM subtree into a html string.
Concatenate some stuff into that html string
Remove all elements from the target element
Parse the html resulted above into a new DOM subtree. This means all the elements are new.
Append that into the target element
So given this, actual_button refers to a detached dom element. Not to the another img element created from parsing html.

Works if you set the image ID and get it after changing innerHTML :
var meaning = document.getElementById('meaning');
meaning.innerHTML += 'Something ...';
var actual_button = document.createElement('img');
actual_button.id = 'actual_button';
actual_button.src = 'http://www.pawelbrewczynski.tk/images/add.png';
actual_button.className = 'add_word';
meaning.appendChild(actual_button);
meaning.innerHTML += " ... and another.";
var actual_button= document.getElementById('actual_button');
actual_button.src = 'http://www.pawelbrewczynski.tk/images/loading.gif';
http://jsfiddle.net/j8yEG/1/

Related

Remove tag and content using Javascript

What I want is to get the content of a specific #id, remove a few tags and its contents, add some html before and after it, and then set that finished content as the body content.
I now have the following code, containing a mixture of Javascript and jQuery, although obviously not the right one - resulting in a [object Object]-message.
My code looks like this:
var printContents = jQuery("#"+id).clone();
var printContentsBefore = '<html><head></head><body><table><tr>';
var printContentsAfter = '</tr></table></body></html>';
var mainContents = printContents.find(".td_print").remove();
document.body.innerHTML = printContentsBefore + mainContents + printContentsAfter;
Any ideas of how to make this work?
Your code does not convert the cloned jquery object to a string. Modify your code as follows:
document.body.innerHTML = printContentsBefore + mainContents.html() + printContentsAfter;
Beware that the .html method output will not include the html representation of the container element itself (ie. of the #id clone in your case).

Prevent jQuery from downloading resources when parsing HTML string

var strHTML = "<div><img src='/fake/path/fakeImage.jpg'/><span id='target'>text to extract</span></div>";
var dom = $(strHTML);
var extractedText = dom.find("#target").text();
alert(extractedText);
When I convert the HTML string to a jQuery object, jQuery makes GET request to retrieve pictures as you can see in the network tab in the developer tools.
JsFiddle
How can I convert a HTML string to jQuery object without downloading any resources from the parsed string ?
Note : jQuery.parseHTML does not return a jQuery object, you cannot use .find() for example.
I don't think this is possible, since its not jQuery (or javascript) that does the image loading but the browser - as soon as a src attribute is set on an img element the browser will attempt to download it.
One thing you can do is change the element name from img to something else before building the dom, or change the src attribute to something else, for example:
// change the <img> elements to <my_img> to avoid image fetching
strHtml = strHtml.replace(/<img /gi, "<my_img ").replace(/<\/img>/gi, "</my_img>");
// or the 2nd alternative: change the src attribute of images
strHtml = strHtml.replace(/<img([^>]*?) src=/gi, "<img$1 my_src=")
// now its safe to parse into DOM - no images will be fetched
var dom = $(strHtml);
note this simple "search and replace" may replace texts other than the elements you want, but it may be sufficient for your use case.
You can feed it through $.parseXML first:
var strHTML = "<div><img src='/fake/path/fakeImage.jpg'/><span id='target'>text to extract</span></div>";
var dom = $($.parseXML(strHTML));
var extractedText = dom.find("#target").text();
alert(extractedText);

Javascript: assign onclick inside class

I created a constructor that will handle a custom list control. I created a method in order to allow the user to add elements to the list, and I need to assign event handlers to the click events of the list elements (divs).
A simplified version of the code is here. The list elements are created using the innerHTML property and a string template upon which I substitute specific parts. Later I get the element by it's id and assign it a function in closure:
function prueba(){
var plantilla = '<div id="«id»">«texto»</div>';
var f = function(nombre){
return function(){console.log('mi nombre es ' + nombre)};
};
this.agregar = function(id, texto){
var tmp = plantilla.replace('«id»', id);
tmp = tmp.replace('«texto»', texto);
document.body.innerHTML += tmp;
document.getElementById(id).onclick = f(id);
};
};
The problem is that, apparently, the event handler is unasigned to previous created divs, so is only retained by the last one, as it can be tested with the following code:
var p = new prueba;
p.agregar('i1', 'texto1');
console.log(document.getElementById('i1').onclick.toString());//shows the function code
p.agregar('i2', 'texto2');
console.log(document.getElementById('i2').onclick.toString());//shows the function code
console.log(document.getElementById('i1').onclick.toString());//returns 'null' error
p.agregar('i3', 'texto3');
console.log(document.getElementById('i3').onclick.toString());//shows the function code
console.log(document.getElementById('i2').onclick.toString());//returns 'null' error
This happens in Iceweasel as well as in Chromium. It does NOT happen when I add 'onclick = f(«id»)' in the template (which I cannot do here because of the assigned function scope), and neither happens if I use document.createElement. What am I doing wrong?
You destroy elements previously created when you do this:
document.body.innerHTML += tmp;
Instead use insertAdjacentHMTL() if you want to append using HTML markup.
document.body.insertAdjacentHTML("beforeend", tmp);
Now instead of going through this destructive process...
serialize the existing DOM nodes to HTML
concatenate the new HTML fragment to the serialized nodes
destroy the old nodes
recreate the nodes with the new nodes
...it simply creates the new content and places it before the close of the body element.
Basically, remove element.innerHTML += ... from your coding practices. It's never necessary, it's inefficient and it causes problems like what you've described.
FYI, the .insertAdjacentHTML() method receives 4 different string possibilities as the first argument. Each one designates a position relative to the element on which you're calling it.
The strings are...
"beforebegin"
"afterbegin"
"beforeend"
"afterend"
The labels are pretty self-explanatory. They position the new content before the current element, inside the current element at the beginning, inside the current element at the end, or after the current element, respectively.
Your full code will look like this, which I shortened a bit too since the tmp really isn't needed here:
function prueba(){
var plantilla = '<div id="«id»">«texto»</div>';
var f = function(nombre){
return function(){console.log('mi nombre es ' + nombre)};
};
this.agregar = function(id, texto){
document.body.insertAdjacentHTML("beforeend",
plantilla.replace('«id»', id)
.replace('«texto»', texto));
document.getElementById(id).onclick = f(id);
};
};

Is there a way to convert HTML into normal text without actually write it to a selector with Jquery?

I understand so far that in Jquery, with html() function, we can convert HTML into text, for example,
$("#myDiv").html(result);
converts "result" (which is the html code) into normal text and display it in myDiv.
Now, my question is, is there a way I can simply convert the html and put it into a variable?
for example:
var temp;
temp = html(result);
something like this, of course this does not work, but how can I put the converted into a variable without write it to the screen? Since I'm checking the converted in a loop, thought it's quite and waste of resource if keep writing it to the screen for every single loop.
Edit:
Sorry for the confusion, for example, if result is " <p>abc</p> " then $(#mydiv).html(result) makes mydiv display "abc", which "converts" html into normal text by removing the <p> tags. So how can I put "abc" into a variable without doing something like var temp=$(#mydiv).text()?
Here is no-jQuery solution:
function htmlToText(html) {
var temp = document.createElement('div');
temp.innerHTML = html;
return temp.textContent; // Or return temp.innerText if you need to return only visible text. It's slower.
}
Works great in IE ≥9.
No, the html method doesn't turn HTML code into text, it turns HTML code into DOM elements. The browser will parse the HTML code and create elements from it.
You don't have to put the HTML code into the page to have it parsed into elements, you can do that in an independent element:
var d = $('<div>').html(result);
Now you have a jQuery object that contains a div element that has the elements from the parsed HTML code as children. Or:
var d = $(result);
Now you have a jQuery object that contains the elements from the parsed HTML code.
You could simply strip all HTML tags:
var text = html.replace(/(<([^>]+)>)/g, "");
Why not use .text()
$("#myDiv").html($(result).text());
you can try:
var tmp = $("<div>").attr("style","display:none");
var html_text = tmp.html(result).text();
tmp.remove();
But the way with modifying string with regular expression is simpler, because it doesn't use DOM traversal.
You may replace html to text string with regexp like in answer of user Crozin.
P.S.
Also you may like the way when <br> is replacing with newline-symbols:
var text = html.replace(/<\s*br[^>]?>/,'\n')
.replace(/(<([^>]+)>)/g, "");
var temp = $(your_selector).html();
the variable temp is a string containing the HTML
$("#myDiv").html(result); is not formatting text into html code. You can use .html() to do a couple of things.
if you say $("#myDiv").html(); where you are not passing in parameters to the `html()' function then you are "GETTING" the html that is currently in that div element.
so you could say,
var whatsInThisDiv = $("#myDiv").html();
console.log(whatsInThisDiv); //will print whatever is nested inside of <div id="myDiv"></div>
if you pass in a parameter with your .html() call you will be setting the html to what is stored inside the variable or string you pass. For instance
var htmlToReplaceCurrent = '<div id="childOfmyDiv">Hi! Im a child.</div>';
$("#myDiv").html(htmlToReplaceCurrent);
That will leave your dom looking like this...
<div id="myDiv">
<div id="childOfmyDiv">Hi! Im a child.</div>
</div>
Easiest, safe solution - use Dom Parser
For more advanced usage - I suggest you try Dompurify
It's cross-browser (and supports Node js). only 19kb gziped
Here is a fiddle I've created that converts HTML to text
const dirty = "Hello <script>in script<\/script> <b>world</b><p> Many other <br/>tags are stripped</p>";
const config = { ALLOWED_TAGS: [''], KEEP_CONTENT: true, USE_PROFILES: { html: true } };
// Clean HTML string and write into the div
const clean = DOMPurify.sanitize(dirty, config);
document.getElementById('sanitized').innerText = clean;
Input: Hello <script>in script<\/script> <b>world</b><p> Many other <br/>tags are stripped</p>
Output: Hello world Many other tags are stripped
Using the dom has several disadvantages. The one not mentioned in the other answers: Media will be loaded, causing network traffic.
I recommend using a regular expression to remove the tags after replacing certain tags like br, p, ol, ul, and headers into \n newlines.

How can I inject a string of HTML into an element?

Using Mootools, we can inject an element into another element:
$('childID').inject($('parentID'), 'top');
The second parameter allows me to control the location and can either be 'top' or 'bottom' to inject it into a parent object or 'before' or 'after' to inject it as a sibling.
We can also set the HTML of an element from a string:
var foo = "<p>Some text</p>";
$('parentID').set('html', foo);
My problem is that I want to have the same flexibility with strings as I do with elements. I can't, for example, put a string at the top of an element using set() as this overwrites the HTML rather than appending it at a specific location. Similarly, I can't append HTML after or before a sibling element.
Is there a function that will allow me to inject strings in the same way as I inject elements?
Insert at bottom:
foo.innerHTML = foo.innerHTML + 'string';
Insert at top:
foo.innerHTML = 'string' + foo.innerHTML;
Best Solution
The inject method will look like this:
inject: function(element, location) {
var el = Elements.from(this);
if($type(el) === 'array') var el = el.reverse();
return el.inject(element, location);
}
Let's break this into parts.
1) Elements.from(this) will take whatever the method is applied to and convert it into elements:
var foo = "<p>Some text</p>";
var el = Elements.from(foo);
//el is equal to a p element.
var bar = "<div>First div</div><div>Second div</div>";
var el = Elements.from(bar);
//el is equal to an array containing 2 div elements
2) if($type(el) === 'array') checks if el is an array. If it is then it applies .reverse() to el. This is necessary to inject the elements in the correct order. Otherwise they would inject with, for example, the second div first and the first div second. Obviously if el is just a single element, we don't need to change its order.
3) Finally, we just use the original inject method to inject el into the element specified in the element parameter to the location specified in the location parameter. If el is an array of elements, they will all get injected just fine.
To be able to use this function, we have to add it as a method on string objects. To do this you have to use implement():
String.implement({
inject: function(element, location) {
var el = Elements.from(this);
if($type(el) === 'array') var el = el.reverse();
return el.inject(element, location);
}
});
This will allow you to use the inject function on any variable containing a string. Make sure you don't put this inside the domready event i.e. Before window.addEvent('domready', function() { ... });
Now the inject function itself will look like this:
var foo = "<p>Some text</p>";
foo.inject($('parentID'), 'top');
This will create the p element and inject it at the top of parentID.
Alternative Solution
If you just wish to use inject with the 'top' and 'bottom' locations, you can use this inject method instead:
inject: function(element, location) {
var html = element.get('html')
if(location === 'top') return element.set('html', this + html);
else if (location === 'bottom') return element.set('html', html + this);
}
This method will get the innerHTML of the element you need to convert and either concatenate the string with that HTML or the HTML with that string, placing the string at the top or the bottom of the element respectively. The element's innerHTML is then set to this value.
The advantage of this method is that as long as the innerHTML of the element isn't too great, this is likely to be faster as we don't need to create new elements which could be time-consuming if the string contains many top-level sibling elements. Obviously if this situation were reversed (few top-level siblings and small innerHTML), the speed advantage would also be reversed (I haven't tested the speed difference so this is just an educated guess and might be negligible).
The disadvantage, however, is that we can't easily use it with the 'after' and 'before' locations.
You're looking for appendText. Example similar to the Mootools docs:
http://mootools.net/docs/core/Element/Element#Element:appendText
HTML
<div id="myElement">partner.</div>
JavaScript
$('myElement').appendText('Howdy, ', 'top');
The second (where) argument defaults to 'bottom' but also accepts 'top', 'bottom', 'before' and 'after'.
Resulting HTML
<div id="myElement">Howdy, partner.</div>
Working example:
http://jsfiddle.net/hq5Gr/
Try this:
var foo = "<p>Some text</p>"
$('parentID').set('html', foo + $('parentID').get('html')); // prepend/top
$('parentID').set('html', $('parentID').get('html') + foo)); // append/bottom
couple of things you ought to look at that may help.
First off, slick and mootools 1.3 offer a "nicer" new Element constructor which can add and configure elements from pseudo string markup very nicely:
http://www.jsfiddle.net/dimitar/aQvpb/
new Element('div#myId.myClass.myOtherClass[title=Mouseover Title][text=Dimitar Was Here]').injectAfter(document.id("foo"));
new Element("input#someID.someClass1.someClass2[disabled=true]");
second of all, element.injectAfter(previousEl) and element.injectBefore(followingEl) can also be helpful in injecting somewhere after or before a particular node.
totally do NOT append html by rewriting old html or any events the elements have that are not delegated will be gone (new UIDs)
and you can use Slick with older versions of mootools as well although I can't find the gist for that atm, post here if you're interested. the currently nightly is fairly stable but 1.3 release is due shortly.
you can use insertAdjacentHTML()
const divInString = '<div class="todo">Stuff</div>'
const parentOfDiv = document.querySelector(".parent")
parentOfDiv.insertAdjacentHTML("beforeEnd",divInString)
// "afterEnd , "beforeBegin","afterBegin"
// I was looking for a solution for this problem as well and
// this is what solved my issue.
You want to use text nodes.
To append text to an element:
var yourTextNode = element.appendChild(document.createTextNode("some text"))
To prepend text to an element:
var yourTextNode = element.parentNode.insertBefore(document.createTextNode("some text"), element)
To change the value of the text node, you'd do yourTextNode.nodeValue = "new value here".
#shanebo's answer is close but appendText escapes HTML.
Try:
http://mootools.net/core/docs/1.5.1/Element/Element#Element:appendHTML
$('myElement').appendHTML('<div>Hello world</div>', 'top');
Fiddle: http://jsfiddle.net/m0ez1t50/1/

Categories