Optimizing javascript (innerhtml, insert elements to textnodes) - javascript

So I'm making a firefox addon to highlight words and reg. expressions and I'm having some troubles optimizing it.
This was the 1st attempt:
function highlight (searchText, replacement) {
var walker = document.createTreeWalker(document.body);
while(walker.nextNode()){
if(walker.currentNode.nodeType === 3 && searchText.test(walker.currentNode.nodeValue)){
var html = walker.currentNode.data.replace(searchText, replacement);
var wrap = document.createElement('div');
var frag = document.createDocumentFragment();
wrap.innerHTML = html;
while (wrap.firstChild) {
frag.appendChild(wrap.firstChild);
}
walker.currentNode.parentNode.replaceChild(frag,walker.currentNode);
}
}
}
But the walker.currentNode.parentNode.replaceChild(frag,walker.currentNode); line replaces the current node so the while(walker.nextNode()) stopped working.
I've solved it like this but i was looking for a cleaner solution:
function highlight (searchText, replacement) {
var walker = document.createTreeWalker(document.body);
var nextnode=true;
while(nextnode){
if(walker.currentNode.nodeType === 3 && searchText.test(walker.currentNode.nodeValue)){
//1~2 ms
var html = walker.currentNode.data.replace(searchText, replacement);
//~11-12 ms
var wrap = document.createElement('div');
var frag = document.createDocumentFragment();
//~11-12 ms
wrap.innerHTML = html;
//~36-37 ms
while (wrap.firstChild) {
frag.appendChild(wrap.firstChild);
}
//73~74 ms
var nodeToReplace=walker.currentNode;
nextnode=walker.nextNode();
nodeToReplace.parentNode.replaceChild(frag,nodeToReplace);
//83~85 ms
}else{
nextnode=walker.nextNode();
}
}
}
Also I'm trying to improve performance so I've made some test to look for the slower parts of the code (I've tested using a 1.64 mb lorem ipsum) so here are my questions:
Is there a faster alternative for the wrap.innerHTML = html; that is adding 25 ms to the code?
I'm pretty sure that this can't be optimized while (wrap.firstChild) {frag.appendChild(wrap.firstChild);} but it adds 37 ms so suggestions are welcome.
Feel free to use this code the snippet is a working example of the code and shows how to use the it.
Edited to show latest changes, you may need to edit the excludes to be less restrictive.
var regexp = /lorem|amet/gi;
highlight (regexp,'<span style="Background-color:#33FF33">$&</span>');
function highlight (searchText, replacement) {
var excludes = 'html,head,style,title,link,script,noscript,object,iframe,canvas,applet';
var wrap = document.createElement('div');
var frag = document.createDocumentFragment();
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
var nextnode=true;
while(nextnode){
if(searchText.test(walker.currentNode.nodeValue)
&& (excludes + ',').indexOf(walker.currentNode.parentNode.nodeName.toLowerCase() + ',') === -1
){
var html = walker.currentNode.data.replace(searchText, replacement);
wrap.innerHTML = html;
while (wrap.firstChild) {
frag.appendChild(wrap.firstChild);
}
var nodeToReplace=walker.currentNode;
nextnode=walker.nextNode();
nodeToReplace.parentNode.replaceChild(frag,nodeToReplace);
}else{
nextnode=walker.nextNode();
}
}
}
<h1>HTML Ipsum Presents</h1>
<p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris
placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis
tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer lorem adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis
elit sit amet quam. Vivamus pretium ornare est.</p>
</blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer lorem adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>

Related

how to replace margin-left to margin-inline-start programmatically in javascript?

I'm working on a multi-language project. I want to change all the used margin-left styles to margin-inline-start, after the direction of the <body> changed according to the selected language. how can I do it programmatically in javaScript?
You probably can try to loop through computed style, retrieve margin-left value, reset margin-left and then apply a margin-inline-start with the value of margin left.
here is the basic idea : (Live Demo at https://codepen.io/gc-nomade/pen/MWrGBjX?editors=1111 )
let allelements = document.querySelectorAll("body *");
for (i = 0; i < allelements.length; i++) {
let computedStyle = window.getComputedStyle(allelements[i]);
// look for
if (computedStyle.getPropertyValue("margin-left") != "0px") {
let valMargin = computedStyle.getPropertyValue("margin-left");
// let's see what's going on, if anything happens
console.log(
"found margin-left. value : " +
computedStyle.getPropertyValue("margin-left") +
" of " +
allelements[i].tagName
);
//reset
allelements[i].style.marginLeft = "auto";
// set/reset inline-start margin
allelements[i].style.marginInlineStart = valMargin;
}
}
body {
direction: rtl;
}
h1,
p,
code {
margin-left: 10em;
}
<h1>HTML Ipsum Presents</h1>
<p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
<pre><code>
#header h1 a {
display: block;
width: 300px;
height: 80px;
}
</code></pre>
I'm not an developper, so that might not be the most effective way to do it.
But starting from here, you will probably find out that margin-right maybe needs to be reset too, and so floats....
If you use a flex or grid-layout, without margin , but justify/align , you will not have to bother about the direction / dir value of the document , the browser will follow it naturally ;)

How to alert all p using javascript?

I learn on how to alert all html code from webpage using this code:
var markup = document.document.innerHTML;
alert(markup);
I want to alert only all <p>
I tried this code
var markup = document.getElementsByTag('p').innerHTML;
alert(markup);`
But it's not working
document.getElementsByTagName("p") returns a HTMLCollection which you can convert to an array using the spread operator. Then you need to get the innerHTML for each element of the array. Finally you can join those innerHTML together to output them:
var pElements = [ ... document.getElementsByTagName("p") ];
var pMarkup = pElements.map( element => element.innerHTML );
alert( pMarkup.join( "\n" ) );
<p>abc<strong>def</strong></p>
<table><tr><td>Don't show this</td></tr></table>
<p>ghi<em>jkl</em></p>
I think you are after HTMLElement.outerHTML
// All p's
const pTags = document.querySelectorAll('p');
const output = [...pTags].map(p => p.outerHTML).join("");
console.log(output);
.red {
color: red;
}
<p class="red">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis at fermentum turpis. Maecenas congue accumsan enim, et dictum turpis malesuada et.
</p>
<p>
Mauris vitae pretium tortor. Aenean nulla ante, scelerisque in erat ac, tincidunt porttitor dolor. Sed blandit sed mi at vulputate.
</p>
<p id="three">
Curabitur lobortis at augue at hendrerit. Mauris id ligula cursus ligula dictum viverra.
</p>
<p>
Sed suscipit varius orci. Duis sit amet fermentum eros. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin commodo turpis in neque aliquam, et laoreet odio consequat.
</p>
<p data-number="5">
Nam dolor neque, lacinia sed viverra et, cursus ac ipsum. Cras gravida quam enim, sit amet tristique urna faucibus non.
</p>
<p>
Phasellus cursus, justo a volutpat pulvinar, ligula metus mollis turpis, in tincidunt ante nisl non nunc.
</p>

Node.js fs cheerio read and write multiple files

I have the following code adapted from here that I am using with Node.js and Cheerio to read html files and split large source files into small chunks. The code is working well for a single file.
Now I need to read multiple large html files and split them one after the other and output the resulting files in a folder.
How can I read and write every file in the folder and then split it?
Here is the code:
var cheerio = require('cheerio'),
fs = require('fs');
fs.readFile('./sourceHtml2/testone.html', 'utf8', dataLoaded);
function dataLoaded(err, data) {
$ = cheerio.load(data);
$('#toplevel > div').each(function (i, elem) {
var id = $(elem).attr('id'),
filename = id + '.html',
content = $.html(elem);
fs.writeFile('./output2/' + filename, content, function (err) {
console.log('Written html to ' + filename);
});
});
}
Here is my sample source file
<!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Lorem Ipsum</title>
</head>
<body>
<div id="toplevel">
<div id="1-1">
<h1>HTML Ipsum Presents One</h1>
<p>
<strong>Pellentesque habitant morbi tristique</strong>senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper.
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
</div>
<div id="1-2">
<h1>HTML Ipsum Presents Two</h1>
<p>
<strong>Pellentesque habitant morbi tristique</strong>senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper.
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus,
at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p>
</blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
</div>
<div id="1-3">
<h1>HTML Ipsum Presents Three</h1>
<p>
<strong>Pellentesque habitant morbi tristique</strong>senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper.
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus,
at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p>
</blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
</div>
</div>
</body>
</html>
Your help will be greatly appreciated.
You need to process the files in the input directory as an array and you'll also want to prevent filename collisions in the output folder.
The code provided below provides a solution to both issues. HTML files (.htm and .html) are read from the 'input' subfolder and the generated files written to the 'output' subfolder.
var cheerio = require('cheerio'),
fs = require('fs');
// process files found in the 'input' folder
fs.readdir('./input', 'utf8', findHtmlFiles);
function findHtmlFiles(err, files) {
if (files.length) {
files.forEach(function (fullFilename) {
var pattern = /\.[0-9a-z]{1,5}$/i;
var ext = (fullFilename).match(pattern);
// only process '.htm' and '.html' files
if (ext[0] == '.htm' || ext[0] == '.html') {
fs.readFile('./input/' + fullFilename, 'utf8', function (err, data) {
if (err)
throw err
else {
// add the file name to prevent collisions
// in the output folder
var fileData = {
file: fullFilename.slice(0, (ext[0].length * -1)),
data: data
};
dataLoaded(null, fileData);
}
});
}
});
}
}
function dataLoaded(err, fd) {
$ = cheerio.load(fd.data);
$('#toplevel > div').each(function (i, elem) {
var id = $(elem).attr('id'),
filename = fd.file + '_' + id + '.html',
content = $.html(elem);
fs.writeFile('./output/' + filename, content, function (err) {
console.log('Written html to ' + filename);
});
});
}
Sample console output:
Written html to testone_1-1.html
Written html to testone_1-2.html
Written html to testone_1-3.html
Written html to testtwo_1-1.html
Written html to testtwo_1-2.html
Written html to testtwo_1-3.html

CSS Transitions not working inside parametrized functions

So I got the following code to create a simple fixed red box:
var red_box = document.createElement('div');
red_box.id = 'caixa_apresentacao_texto';
red_box.style.width = "40%";
red_box.style.overflow = "hidden";
red_box.style.backgroundColor = "white";
red_box.style.color = "black";
red_box.style.border = "5px double red";
/* Centralizing */
red_box.style.position = "fixed";
red_box.style.left = "50%";
red_box.style.marginLeft = "-20%"; //Por que a largura é 40%...
red_box.style.transition = "max-height 1s";
red_box.style.display = "none";
red_box.style.zIndex = "99999999999999";
red_box.style.marginTop = "50px";
red_box.style.maxHeight = "0px";
document.documentElement.insertBefore(red_box,document.body);
So, the idea is that, when I pass some text to this box, it enlarges slowly in order to display it. I get this behaviour with the following code:
var timerHeight;
function expandBox(text){
clearInterval(timerHeight);
/* if the box is empty...*/
if(document.querySelector("#red_box").style.maxHeight == "0px"){
document.querySelector("#red_box").style.display = "inline-block";
red_box.innerHTML = text;
/* Call a function that enlarge the maxHeight property , theorically with the transition letting it more beautiful */
var someText = "text";
timerHeight= setTimeout(enlargeBoxHeight(someText),1);
}
}
function enlargeBoxHeight(anyText){
document.querySelector("#red_box").style.maxHeight ="50px";
}
expandBox("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sollicitudin euismod metus, at blandit neque maximus ac. Integer fermentum nulla at nibh suscipit, a placerat est pretium. Morbi varius ornare enim, ac pulvinar elit aliquet in. Nullam non diam in nibh consectetur fringilla id nec enim. Mauris lacinia a augue ac consectetur. Etiam tempor et elit a dictum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam interdum pulvinar pharetra. Aliquam erat volutpat. Aliquam non diam eget turpis tincidunt venenatis at in est. Duis laoreet nibh ultrices erat faucibus hendrerit.")
You can see the fiddle here.
So, I know that 50px is not a good height, but what matters here is that the transition is not working. You may have noticed that the var someText is useless here; but it does have the purpose to express my doubt. I've tried to take it off of the enlargeBoxHeight call. So the last part of the code now is:
...
timerHeight= setTimeout(enlargeBoxHeight,1);
}
}
function enlargeBoxHeight(){
document.querySelector("#red_box").style.maxHeight ="50px";
}
expandBox("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sollicitudin euismod metus, at blandit neque maximus ac. Integer fermentum nulla at nibh suscipit, a placerat est pretium. Morbi varius ornare enim, ac pulvinar elit aliquet in. Nullam non diam in nibh consectetur fringilla id nec enim. Mauris lacinia a augue ac consectetur. Etiam tempor et elit a dictum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam interdum pulvinar pharetra. Aliquam erat volutpat. Aliquam non diam eget turpis tincidunt venenatis at in est. Duis laoreet nibh ultrices erat faucibus hendrerit.")
And the surprise: the transition works now. Why? What I am missing here?
When you do:
setTimeout(enlargeBoxHeight,1);
Without the parentheses (), you pass the function enlargeBoxHeight to the timeout, without calling it yet. The timeout will call it after 1ms. Which produced expected behavior + transition.
When you do:
timerHeight= setTimeout(enlargeBoxHeight(someText),1);
You pass the result of the function enlargeBoxHeight to the timeout. () part forces javascript to immediately call the function, and process everything before the timeout. So the transition does not work. After the 1ms, javascript handles the result (with is undefined or irrelevant).
If you want to pass a parameter to a timeout, do:
timerHeight= setTimeout(enlargeBoxHeight.bind(someText),1);
Which should work as expected.

Wrap unwrapped parts of HTML string with Javascript

I have string in variable (Javascript/jQuery) containing content like this:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<p>Morbi a faucibus magna. Donec lacinia, leo eget</p>
Pellentesque aliquet luctus lobortis.
<p>Morbi a faucibus magna. Donec lacinia, leo eget</p>
massa iaculis leo, nec auctor
how i can wrap all unwrapped content in p tags?
So that string looks like:
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<p>Morbi a faucibus magna. Donec lacinia, leo eget</p>
<p>Pellentesque aliquet luctus lobortis.</p>
<p>Morbi a faucibus magna. Donec lacinia, leo eget</p>
<p>massa iaculis leo, nec auctor</p>
Thank you!
Something like
var str = 'your string';
var div = $('<div />', {html: str});
div.contents().filter(function() {
return this.nodeType === 3;
}).wrap('<p />');
var new_str = div.html();
FIDDLE
Using a new jQuery object to parse the string as HTML, and then filtering out unwrapped textnodes, and wrapping them with paragraphs, and outputting the changed HTML as the new string.
Here's a jQuery-free way to do it using only string methods (no DOM required.)
var text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.<p>Morbi a faucibus magna. Donec lacinia, leo eget</p>Pellentesque aliquet luctus lobortis.<p>Morbi a faucibus magna. Donec lacinia, leo eget</p>massa iaculis leo, nec auctor",
unwrapped = text.split(/<p>\b[^>]*<\/p>/g), //regex to split on all p wrapped text
i;
for (i=0; i < unwrapped.length; i++) {
text = text.replace(unwrapped[i], '<p>' + unwrapped[i] + '</p>');
};

Categories

Resources