extract div as HTML including CSS classes - javascript

I am trying to create a general function that will extract a div content (with nested elements) and save it locally in an HTML file.
Basically I get the div innerHTML, wrap it in html/head/body tags and then save it:
function div2html() {
var inner=document.getElementById("div2save").innerHTML;
var html="<html><head></head><body>"+inner+"</body></html>";
saveTextAsFile("div2html.html", html);
}
See a working version here: jsfiddle
However I am not sure how to handle classes. As you can see the class in the sample (bigbold) is not embedded in the new HTML. I need some way to get all the classes used in the div and then add them (or the computed styles ?) to the html I generate .. is this possible ? is there any other way around it ?

Try including style element .outerHTML within saved html
function div2html() {
var inner=document.getElementById("div2save").innerHTML;
var style = document.getElementsByTagName("style")[0].outerHTML;
var html="<html><head>"+style+"</head><body>"+inner+"</body></html>";
saveTextAsFile("div2html.html", html);
}
jsfiddle http://jsfiddle.net/fb6s763w/1/
Alternatively, using window.getComputedStyle() to select only css of #div2save child node
function div2html() {
var inner = document.getElementById("div2save");
var style = window.getComputedStyle(inner.children[0]).cssText;
var html = "<html><head><style>"
+ "." + inner.children[0].className
+ "{" + style + "}"
+ "</style></head><body>"
+ inner.innerHTML + "</body></html>";
saveTextAsFile("div2html.html", html);
}
jsfiddle http://jsfiddle.net/fb6s763w/2/

Looks like this might be able to help you out:
https://github.com/Automattic/juice

If the CSS of the page is not big, a simple solution is to include it all in the saved html as suggested by guest271314 above with
var style = document.getElementsByTagName("style")[0].outerHTML;
see jsfiddle
A more comprehensive solution extracts the classes from the div and then adds only the rules of those classes to the div (Using code from How do you read CSS rule values with JavaScript?)
function div2html(divId) {
var html = document.getElementById(divId).innerHTML;
// get all css classes in html
var cssClasses = [];
var classRegexp = /class=['"](.*?)['"]/g;
var m;
while ((m = classRegexp.exec(html))) cssClasses = cssClasses.concat(cssClasses, m[1].split(" "));
// filter non unique or empty cssClasses
cssClasses = cssClasses.filter(function (item, pos, self) {
return item && self.indexOf(item) == pos;
});
// get html of classes
var cssHtml = '';
for (var i = 0; i < cssClasses.length; i++) cssHtml += getRule('.' + cssClasses[i]);
// assemble html
var html = "<html><head><style>" + cssHtml + "</style></head><body>" + html + "</body></html>";
console.log(html);
saveTextAsFile("div2html.html", html);
}
see jsfiddle

Related

jQuery code not working for Instagram-clone-like course project

For a course assignment, I'm tasked with 'fixing' an instagram-like page that isn't working properly.
I need to render the hashtags from a tags list by creating an 'a' tag and an 'li' tag. eg. '#beachday (4)'
this is my (unsuccessful) code currently:
I assume the tags should show up with the photos that have been rendered on the page.
function renderTags (tags) {
var tagList = $('.tag-list ul');
tags.forEach(function (tag)
var a = $('<a>').text('#' + tag.tags + '(' + tag.tag_count + ')');
var li = $('<li>').addClass('u-pull-left');
li.append(a);
tagList.append(li);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>Missing HTML here perhaps?</div>
I'm only beginning to learn CSS/html/jQuery so any help understanding where I'm wrong, or a point to any resource (looked at jQuery docs) is greatly appreciated.
~thank you
As I write the javascript code, the LiveServer webpage should reflect the code and changes and at the moment none of my edits have produced results.
1.Ensure the forEach method is properly wrapped in brackets
2.Attach the 'href' attribute to the 'a' tag to make it clickable
function renderTags (tags) {
var tagList = $('.tag-list ul');
tags.forEach(function (tag) {
var a = $('<a>').text('#' + tag.tags + '(' + tag.tag_count + ')');
a.attr('href', '#');
var li = $('<li>').addClass('u-pull-left');
li.append(a);
tagList.append(li);
});
}

Highlighting and formatting code snippets using JavaScript

I'm looking for a way to highlight and format code snippets passed as string for a live style guide. I'm playing around with highlighjs and prettify. They are really helpful and easy for highlighting, but I can't seem to figure out a way to format or whether they can actually do that or not.
By formatting, I mean tabs and newlines to make code legible. I need to pass code as a string to automate the output of dust template I'm using for the style guide.
That is, I want to pass:
"<table><tr><td class="title">Name</td><td class="title">Category</td><td class="title">Results</td></tr></table>"
And get something like:
<table>
<tr>
<td class="title">Name</td>
<td class="title">Category</td>
<td class="title">Results</td>
</tr>
</table>
Any ideas on how to accomplish this?
Thanks!
You could parse this as HTML into a DOM and than traverse every element writing it out and indenting it with every iteration.
This code will do the job. Feel free to use it and surely to improve it. It's version 0.0.0.1.
var htmlString = '<table><tr><td class="title">Name</td><td class="title">Category</td><td class="title">Results</td></tr></table>';
//create a containing element to parse the DOM.
var documentDOM = document.createElement("div");
//append the html to the DOM element.
documentDOM.insertAdjacentHTML('afterbegin', htmlString);
//create a special HTML element, this shows html as normal text.
var documentDOMConsole = document.createElement("xmp");
documentDOMConsole.style.display = "block";
//append the code display block.
document.body.appendChild(documentDOMConsole);
function indentor(multiplier)
{
//indentor handles the indenting. The multiplier adds \t (tab) to the string per multiplication.
var indentor = "";
for (var i = 0; i < multiplier; ++i)
{
indentor += "\t";
}
return indentor;
}
function recursiveWalker(element, indent)
{
//recursiveWalker walks through the called DOM recursively.
var elementLength = element.children.length; //get the length of the children in the parent element.
//iterate over all children.
for (var i = 0; i < elementLength; ++i)
{
var indenting = indentor(indent); //set indenting for this iteration. Starts with 1.
var elements = element.children[i].outerHTML.match(/<[^>]*>/g); //retrieve the various tags in the outerHTML.
var elementTag = elements[0]; //this will be opening tag of this element including all attributes.
var elementEndTag = elements[elements.length-1]; //get the last tag.
//write the opening tag with proper indenting to the console. end with new line \n
documentDOMConsole.innerHTML += indenting + elementTag + "\n";
//get the innerText of the top element, not the childs using the function getElementText
var elementText = getElementText(element.children[i]);
//if the texts length is greater than 0 put the text on the page, else skip.
if (elementText.length > 0)
{
//indent the text one more tab, end with new line.
documentDOMConsole.innerHTML += (indenting + indentor(1) ) + elementText+ "\n";
}
if (element.children[i].children.length > 0)
{
//when the element has children call function recursiveWalker.
recursiveWalker(element.children[i], (indent+1));
}
//if the start tag matches the end tag, write the end tag to the console.
if ("<"+element.children[i].nodeName.toLowerCase()+">" == elementEndTag.replace(/\//, ""))
{
documentDOMConsole.innerHTML += indenting + elementEndTag + "\n";
}
}
}
function getElementText(el)
{
child = el.firstChild,
texts = [];
while (child) {
if (child.nodeType == 3) {
texts.push(child.data);
}
child = child.nextSibling;
}
return texts.join("");
}
recursiveWalker(documentDOM, 1);
http://jsfiddle.net/f2L82m8h/

Change existing DIV from a CLASS to an ID

Is this possible? Or is there a way to tack on and ID to an existing div?
This is my code. I can't get the code to work using classes, but I found when I used getElementById and changed the div to an ID, that it did. But I have a ton of already posted stuff so it would take forever to go through all those posts and change it manually to an ID.
Can I incorperate JQuery in this and still have it work? I tried that with something I stumbled across but it didn't work so I removed it. I don't remember what it is now though. :S
<div id="imdb" class="imdb">tt2382396</div>
<script>
function imdbdiv() {
var imdbmain = "http://www.imdb.com/title/";
var end = "/#overview-top";
var idnum = document.getElementsByClassName("imdb");
var newdiv = document.createElement("div");
var done = "<a href='" + imdbmain + idnum + end + "'>IMDB</a>";
newdiv.innerHTML = done;
document.body.appendChild(newdiv);
}
window.onload = imdbdiv();
</script>
Can anyone help. I cannot for the life of me figure this out.
JsFiddle
Your problem was, you were appending the collection returned by document.getElementsByClassName instead of looping through the elements in the collection. You can verify this by looking at the href property of the link in your jsFiddle. You must loop through the values, then access the data in their innerHTML property.
You can use document.querySelectorAll to get a list of all elements matching a certain CSS selector, in your case .imdb. This is more flexible, in case you want to select elements with more than one class. I've pasted the code from the updated jsFiddle below.
function imdbdiv() {
var imdbMain = "http://www.imdb.com/title/",
end = "/#overview-top",
imdbValueDivs = document.querySelectorAll('.imdb'),
length = imdbValueDivs.length,
// Iterator values
i,
newDiv,
newLink;
// Loop over all of your link value containers
for (i = 0; i < length; i++) {
// Create the container
newDiv = document.createElement('div');
// Create the new link
newLink = document.createElement('a');
newLink.href = imdbMain + imdbValueDivs[i].innerHTML + end;
newLink.innerHTML = "My favorite film";
// Add the link to the container,
// and add the container to the body
newDiv.appendChild(newLink);
document.body.appendChild(newDiv);
}
}
window.onload = imdbdiv();
If you have many such divs on your page, then it could be like this:
<div class="imdb">tt2382396</div>
<div class="imdb">tt2382396</div>
<div class="imdb">tt2382396</div>
<script>
function imdbdiv() {
var imdbmain = "http://www.imdb.com/title/";
var end = "/#overview-top";
var idnums = document.getElementsByClassName("imdb");
for (var i =0; i < idnums.length; i++) {
var newdiv = document.createElement("div");
var done = "<a href='" + imdbmain + idnums[i].innerText + end + "'>IMDB</a>";
newdiv.innerHTML = done;
document.body.appendChild(newdiv);
}
}
window.onload = imdbdiv();
</script>
See jsfiddle
UPDATE:
The following string was incorrect:
window.onload = imdbdiv;
Okay, so your question is a little bit unclear.
The way I understood your question is that you have a whole bunch of div elements with class attribute and what you want is to simply copy the class value to the id attribute of the div elements.
If that's correct then try something like this with jquery:
<script>
$(document).ready(function(){
$(".imdb").each(function(imdbDiv){
var classValue = imdbDiv.attr("class");
imdbDiv.attr("id", classValue);
});
});
</script>

Show image in JavaScript

I got a litlle js code that is showing me updates from a feed
google.load("feeds", "1");
function initialize() {
var feed = new google.feeds.Feed("http://google.com/");
feed.setNumEntries(1);
var count = 1;
feed.load(function(result) {
if (!result.error) {
var container = document.getElementById("feed");
var html = "";
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
html = "<h5>" + count++ + ". <a href='" + entry.link + "'>" + entry.title + "</a></h5>";
var div = document.createElement("div");
div.innerHTML = html;
container.appendChild(div);
}
document.write(html);
}
});
}
google.setOnLoadCallback(initialize);
What i want to do is to show the first image from the posts from the feed. i would also like to have the title so entry.title and entry.content
Even though parsing html with regx is big dodo, I will still advise you this, parse your content html with /<img\s+src\s*=\s*(["'][^"']+["']|[^>]+)>/
Or a lazy way is to have a hidden div and do this
var temp = document.createElement( 'div' );
temp.innerHTML = html_str;
var images = temp.getElementsByTagName( 'img' );
First, you must use entry.content instead of entry.title in order to get the full HTML content of the entry. You may have something like this:
var content = entry.content;
var imgArray = content.match( /<img\s+src\s*=\s*(["'][^"']+["']|[^>]+)>/ );
// imgArray[0] would contain the first image (more likely the one that better describe the post)
P.S.: I didn't steal this Regex from actual answer, but it seems that we've got to the same reference for it :-)
UPDATE:
Then, to display it in a container, I would advise you to dynamically create DOM elements to gain more control over it, and in which you will be able to easily associate a value. Something like this:
var dom_h5 = document.createElement('h5');
var dom_entryTitle = document.createElement('div');
dom_entryTitle.className = 'title-classname';
dom_entryTitle.innerHTML = entry.title;
dom_h5.appendChild(dom_entryTitle);
container.appendChild(dom_h5);
To simplify the image part, you could create a separate div and inject the image tag as his innerHTML.
This may help you:
Web API Reference - document.createElement

jQuery: how to change tag name?

jQuery: how to change tag name?
For example:
<tr>
$1
</tr>
I need
<div>
$1
</div>
Yes, I can
Create DOM element <div>
Copy tr content to div
Remove tr from dom
But can I make it directly?
PS:
$(tr).get(0).tagName = "div";
results in DOMException.
You can replace any HTML markup by using jQuery's .replaceWith() method.
example: http://jsfiddle.net/JHmaV/
Ref.: .replaceWith
If you want to keep the existing markup, you could use code like this:
$('#target').replaceWith('<newTag>' + $('#target').html() +'</newTag>')
No, it is not possible according to W3C specification: "tagName of type DOMString, readonly"
http://www.w3.org/TR/DOM-Level-2-Core/core.html
Where the DOM renameNode() Method?
Today (2014) no browser understand the new DOM3 renameNode method (see also W3C)
check if run at your bowser: http://jsfiddle.net/k2jSm/1/
So, a DOM solution is ugly and I not understand why (??) jQuery not implemented a workaround?
pure DOM algorithm
createElement(new_name)
copy all content to new element;
replace old to new by replaceChild()
is something like this,
function rename_element(node,name) {
var renamed = document.createElement(name);
foreach (node.attributes as a) {
renamed.setAttribute(a.nodeName, a.nodeValue);
}
while (node.firstChild) {
renamed.appendChild(node.firstChild);
}
return node.parentNode.replaceChild(renamed, node);
}
... wait review and jsfiddle ...
jQuery algorithm
The #ilpoldo algorithm is a good start point,
$from.replaceWith($('<'+newname+'/>').html($from.html()));
As others commented, it need a attribute copy ... wait generic ...
specific for class, preserving the attribute, see http://jsfiddle.net/cDgpS/
See also https://stackoverflow.com/a/9468280/287948
The above solutions wipe out the existing element and re-create it from scratch, destroying any event bindings on children in the process.
short answer: (loses <p/>'s attributes)
$("p").wrapInner("<div/>").children(0).unwrap();
longer answer: (copies <p/>'s attributes)
$("p").each(function (o, elt) {
var newElt = $("<div class='p'/>");
Array.prototype.slice.call(elt.attributes).forEach(function(a) {
newElt.attr(a.name, a.value);
});
$(elt).wrapInner(newElt).children(0).unwrap();
});
fiddle with nested bindings
It would be cool to copy any bindings from the at the same time, but getting current bindings didn't work for me.
To preserve the internal content of the tag you can use the accessor .html() in conjunction with .replaceWith()
forked example: http://jsfiddle.net/WVb2Q/1/
Inspired by ericP answer, formatted and converted to jQuery plugin:
$.fn.replaceWithTag = function(tagName) {
var result = [];
this.each(function() {
var newElem = $('<' + tagName + '>').get(0);
for (var i = 0; i < this.attributes.length; i++) {
newElem.setAttribute(
this.attributes[i].name, this.attributes[i].value
);
}
newElem = $(this).wrapInner(newElem).children(0).unwrap().get(0);
result.push(newElem);
});
return $(result);
};
Usage:
$('div').replaceWithTag('span')
Working pure DOM algorithm
function rename_element(node, name) {
let renamed = document.createElement(name);
Array.from(node.attributes).forEach(attr => {
renamed.setAttribute(attr.name, attr.value);
})
while (node.firstChild) {
renamed.appendChild(node.firstChild);
}
node.parentNode.replaceChild(renamed, node);
return renamed;
}
You could go a little basic. Works for me.
var oNode = document.getElementsByTagName('tr')[0];
var inHTML = oNode.innerHTML;
oNode.innerHTML = '';
var outHTML = oNode.outerHTML;
outHTML = outHTML.replace(/tr/g, 'div');
oNode.outerHTML = outHTML;
oNode.innerHTML = inHTML;
To replace the internal contents of multiple tags, each with their own original content, you have to use .replaceWith() and .html() differently:
http://jsfiddle.net/kcrca/VYxxG/
JS to change the tag name
/**
* This function replaces the DOM elements's tag name with you desire
* Example:
* replaceElem('header','ram');
* replaceElem('div.header-one','ram');
*/
function replaceElem(targetId, replaceWith){
$(targetId).each(function(){
var attributes = concatHashToString(this.attributes);
var replacingStartTag = '<' + replaceWith + attributes +'>';
var replacingEndTag = '</' + replaceWith + '>';
$(this).replaceWith(replacingStartTag + $(this).html() + replacingEndTag);
});
}
replaceElem('div','span');
/**
* This function concats the attributes of old elements
*/
function concatHashToString(hash){
var emptyStr = '';
$.each(hash, function(index){
emptyStr += ' ' + hash[index].name + '="' + hash[index].value + '"';
});
return emptyStr;
}
Related fiddle is in this link
Since replaceWith() didn't work for me on an element basis (maybe because I used it inside map()), I did it by creating a new element and copying the attributes as needed.
$items = $('select option').map(function(){
var
$source = $(this),
$copy = $('<li></li>'),
title = $source.text().replace( /this/, 'that' );
$copy
.data( 'additional_info' , $source.val() )
.text(title);
return $copy;
});
$('ul').append($items);
Take him by the word
Taken the Question by Word "how to change tag name?" I would suggest this solution:
If it makes sense or not has to be decided case by case.
My example will "rename" all a-Tags with hyperlinks for SMS with span tags. Maintaining all attributes and content:
$('a[href^="sms:"]').each(function(){
var $t=$(this);
var $new=$($t.wrap('<div>')
.parent()
.html()
.replace(/^\s*<\s*a/g,'<span')
.replace(/a\s*>\s*$/g,'span>')
).attr('href', null);
$t.unwrap().replaceWith($new);
});
As it does not make any sense to have a span tag with an href attribute I remove that too.
Doing it this way is bulletproof and compatible with all browsers that are supported by jquery.
There are other ways people try to copy all the Attributes to the new Element, but those are not compatible with all browsers.
Although I think it is quite expensive to do it this way.
Jquery plugin to make "tagName" editable :
(function($){
var $newTag = null;
$.fn.tagName = function(newTag){
this.each(function(i, el){
var $el = $(el);
$newTag = $("<" + newTag + ">");
// attributes
$.each(el.attributes, function(i, attribute){
$newTag.attr(attribute.nodeName, attribute.nodeValue);
});
// content
$newTag.html($el.html());
$el.replaceWith($newTag);
});
return $newTag;
};
})(jQuery);
See : http://jsfiddle.net/03gcnx9v/3/
Yet another script to change the node name
function switchElement() {
$element.each(function (index, oldElement) {
let $newElement = $('<' + nodeName + '/>');
_.each($element[0].attributes, function(attribute) {
$newElement.attr(attribute.name, attribute.value);
});
$element.wrapInner($newElement).children().first().unwrap();
});
}
http://jsfiddle.net/rc296owo/5/
It will copy over the attributes and inner html into a new element and then replace the old one.
$(function(){
$('#switch').bind('click', function(){
$('p').each(function(){
$(this).replaceWith($('<div/>').html($(this).html()));
});
});
});
p {
background-color: red;
}
div {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Hello</p>
<p>Hello2</p>
<p>Hello3</p>
<button id="switch">replace</button>
You can use this function
var renameTag = function renameTag($obj, new_tag) {
var obj = $obj.get(0);
var tag = obj.tagName.toLowerCase();
var tag_start = new RegExp('^<' + tag);
var tag_end = new RegExp('<\\/' + tag + '>$');
var new_html = obj.outerHTML.replace(tag_start, "<" + new_tag).replace(tag_end, '</' + new_tag + '>');
$obj.replaceWith(new_html);
};
ES6
const renameTag = function ($obj, new_tag) {
let obj = $obj.get(0);
let tag = obj.tagName.toLowerCase();
let tag_start = new RegExp('^<' + tag);
let tag_end = new RegExp('<\\/' + tag + '>$');
let new_html = obj.outerHTML.replace(tag_start, "<" + new_tag).replace(tag_end, '</' + new_tag + '>');
$obj.replaceWith(new_html);
};
Sample code
renameTag($(tr),'div');
Try this one also. in this example we can also have attributes of the old tag in new tag
var newName = document.querySelector('.test').outerHTML.replaceAll('h1', 'h2');
document.querySelector('.test').outerHTML = newName;
<h1 class="test">Replace H1 to H2</h1>

Categories