Javascript - Strip certain strings from ".innerHTML" result - javascript

I have a function that's run on a button click. This function will get all of the HTML inside a certain element. That works fine. However, I would like to clean the returned string (HTML) up before using it further in my function:
exportHTML(){
const element = document.getElementById('content');
const innerHTML = element.innerHTML;
}
This works. But due to using Angular, Angular syntax is included within the HTML based on conditions in the source code. For example:
<div _ngcontent-c1=""></div>
OR
<div ng-reflect-klass="panel album"></div>
<div ng-reflect-ng-class="blue"></div>
Is it at all possible to filter these types of values out? In regards to the second and third example above, the classes within those would change quite often:
Is it possible to filter out and remove all _ngcontent-c1="" text
Is it possible to filter out and remove all ng-reflect-klass & ng-reflect-ng-class including the following open and closed quotes (to remove what's inside)

OK, so the attributes would be constant but the values of the attributes would change? If so, you could try this:
.replace(/ng-reflect-klass=\".?\"/,"").replace(/ng-reflect-ng-class=\".?\"/,"").replace(/_ngcontent-c1=\".*?\"/,"")
var content = 'stuff<div ng-reflect-klass="panel album"></div><div ng-reflect-ng-class="blue"></div><div _ngcontent-c1=""></div>end stuff';
console.log(content.replace(/ng-reflect-klass=\".*?\"/g,"").replace(/ng-reflect-ng-class=\".*?\"/g,"").replace(/_ngcontent-c1=\".*?\"/g,""));
Look at the console to view the result.

You could do it with RegExp
const innerHTML = element.innerHTML.replace(/ (_ngcon|ng-).*?".*?"/g, '');
(_ngcon|ng-) find _ngcon or ng- including space as first character
.*?" match everything until first "
.*?" and match everything again for the closing "

I created a JSFiddle as an example of how to do this without using jQuery.
Using the HTML code below as an example
<div id="origin-content">
<div id="div1" _ngcontent-c1="">Content 1</div>
<div id="div2" ng-reflect-klass="panel album">Content 2</div>
<div id="div3" ng-reflect-ng-class="blue">Content 3</div>
</div>
<hr>
<div id="target-content">
</div>
I extracted all children from origin-content and copied them to target-content using the code that follows.
var result = document.getElementById('target-content');
var elems = document.querySelector('#origin-content').children;
var count = elems.length;
for (var i = 0; i < count; i++) {
var val = elems[i];
val.removeAttribute('_ngcontent-c1');
val.removeAttribute('ng-reflect-klass');
val.removeAttribute('ng-reflect-ng-class');
result.innerHTML += val.outerHTML;
}
There is still plenty of room for improvement.
I hope it helps to solve the OP question.

The following solution will remove all the attributes from element:
You can get all the children first. Then loop through them with forEach(). In each iteration, you can use while loop to removeAttribute() until they are exist in the element.
Try the following way:
function exportHTML(){
const element = document.getElementById('content');
const innerHTML = [].slice.call(element.children);
innerHTML.forEach(function(el){
while(el.attributes.length > 0)
el.removeAttribute(el.attributes[0].name);
});
console.log(document.getElementById('content').innerHTML); // output
}
exportHTML();
<div id="content">
<div _ngcontent-c1=""></div>
<div ng-reflect-klass="panel album"></div>
<div ng-reflect-ng-class="blue" another-test></div>
<span test="test-element"></span>
</div>

Related

How do I get a string that's the text() of an element, but with spaces added after divs?

JSFiddle here
Hi! I'm trying to output a string from the .contents().text() of an element... but with spaces between the content of each div (without changing the actual DOM).
HTML:
<!-- I don't have control over how many divs are in .myTextArea, or what text. It's really dynamic. There are also lists, etc.--tons of different types of elements. -->
<div class="myTextArea">
<div>Hey there!</div><div>I like turtles.</div><div>Do you like them?</div>
</div>
jQuery:
var myTextDescription = $(".myTextArea").contents().text();
console.log(myTextDescription);
Currently, it outputs:
Hey there!I like turtles.Do you like them?
...and this is what I want it to output: The same thing, but with spaces after the content of each div:
Hey there! I like turtles. Do you like them?
Note: Other answers on SO make you change the actual DOM (AKA, they add actual spaces after the elements on the page), and then they just grab the text() string. I don't want to change the DOM.
Also, I can't use .html() instead and try to strip away stuff, because there will be wayyyyyy too many types of elements to worry about.
JSFiddle here
You're almost there. Replace .text() with:
//get text content of all nodes
.map((i,d) => d.textContent).get()
//remove white space
.filter(t => !!t.trim())
//join the text from all nodes with a space
.join(' ');
Check out the demo below:
var myTextDescription = $(".myTextArea").contents().map((i,d) => d.textContent).get().filter(t => !!t.trim()).join(' ');
console.log(myTextDescription);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="myTextArea">
<div>Hey there!</div><div>I like turtles.</div><div>Do you like them?</div>
</div>
In case you needed to exclude text in a div, say with a class exclude you can use the :not() psedo selector like so:
... .contents(':not(".exclude")') ....
..as in the demo below:
var myTextDescription = $(".myTextArea").contents(':not(".exclude")').map((i,d) => d.textContent).get().filter(t => !!t.trim()).join(' ');
console.log(myTextDescription);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="myTextArea">
<div>Hey there!</div><div class="exclude">Please exclude this!</div><div>I like turtles.</div><div>Do you like them?</div><div class="exclude">Please exclude this too!</div>
</div>
One way to solve this is to use JavaScript's querySelectorAll method to return a list of all the DIVs in your myTextArea element. You can then run through each element in the list, get its innerText and place a space after each one:
var myTexts = document.querySelectorAll('.myTextArea > div');
var show = document.querySelector('#show');
var output = "";
for (var x = 0; x < myTexts.length; x++) {
if (output == "") { // Skip adding space before first string
output = myTexts[x].innerText;
} else { // Add space before each appended string
output += " " + myTexts[x].innerText;
}
}
show.innerText = output;
<div class="myTextArea">
<div>Hey there!</div>
<div>I like turtles.</div>
<div>Do you like them?</div>
</div>
<div id="show"></div>

Html selector returns an html collection and I don't know how to get to the element I need to make changes on

I have 2 divs: 1 on the left half of the page (A), one on the right (B). When hovering over a certain element of the right section, I want something to be displayed over the left one.
I did this using the following approach:
<div className="A">
<div className="hidden-div1">DIV 1</div>
<div className="hidden-div2">DIV 2</div>
<div className="hidden-div3">DIV 3</div>
</div>
<div className="B">
<div className="base-div1">
<h2 onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>Project 1</h2>
</div>
</div>
mouseOver(e){
const hiddenDiv1 = document.querySelector(".hidden-div1");
hiddenDiv1.style.display = "block";
}
mouseOut(e){
const hiddenDiv1 = document.querySelector(".hidden-div1");
hiddenDiv1.style.display = "none";
}
Problem is, considering I have 3 different hidden-divs and 3 different base-divs, I wanted to make 2 universal mouseOver and mouseOut functions for all of them. The way I tried it, is this:
mouseOver(e){
let hiddenDivName = "hidden-div" + e.target.className.slice(-1);
let hiddenDivSelector = document.getElementsByClassName(hiddenDivName);
hiddenDivSelector.style.display = "block";
}
but it returns "Cannot set property 'display' of undefined".
I tried console logging hiddenDivSelector and it shows an HTML collection and I don't know how to get my element. I've tried reading about it and visiting other questions but I couldn't apply anything to my situation
Event target returns a reference to DOM element. On DOM elements we can use getAttribute method and replace all non-digit characters by ''; result may be used to search DOM and iterate over returned array;
mouseOver(e){
let hiddenDivName = "hidden-div" + e.target.getAttribute('class').replace(/\D/g, '');
let hiddenDivSelector = document.getElementsByClassName(hiddenDivName);
Array.from( hiddenDivSelector ).forEach(el => el.style.display ) = "block";
}

Move div content into another div using pure javascript

I have this HTML setup:
<div class="one">
<div class="text">One</div>
<div class="text">One</div>
</div>
<div class="two">
<div class="text">Two</div>
<div class="text">Two</div>
</div>
I want to move the content of div .two into .one using pure javascript (not jQuery) so we get:
<div class="one">
<div class="text">One</div>
<div class="text">One</div>
<div class="text">Two</div>
<div class="text">Two</div>
</div>
What is the best way to do this with millisecond performance in mind?
I personally prefer insertAdjacentElement as it gives you more control as to where to put elements, but be careful to take note of its browser support.
const one = document.querySelector('.one');
const two = document.querySelector('.two');
[...two.children].forEach(element => {
one.insertAdjacentElement('beforeEnd', element);
});
<div class="one">
<div class="text">One</div>
<div class="text">One</div>
</div>
<div class="two">
<div class="text">Two</div>
<div class="text">Two</div>
</div>
Also note that I've used ES2015 syntax.
The possible duplicate question actually has a native answer - use .appendChild() to move the nodes.
In your case, the code would look like this:
var one = document.querySelector(".one");
var children = document.querySelector(".two").children;
Array.prototype.forEach.call(children, function (child) {
one.appendChild(child);
});
You can loop over it with a while loop and use a DocumentFragment if you're after the performance boost.
var one = document.querySelector(".one");
var children = document.querySelector(".two").children;
var frag = document.createDocumentFragment();
while (children.length) {
frag.appendChild(children[0]);
}
one.appendChild(frag);
Faster solution (source):
var one = document.querySelector(".one");
var children = [...document.querySelector(".two").children];
var frag = document.createDocumentFragment();
var i = 0;
var il = children.length;
while (i < il) {
frag.appendChild(children[i]);
i += 1;
}
one.appendChild(frag);
Pure Javascript, compatible with most browser and old versions, use querySelector as its better in performance to get the first found class instead getElementsByClassName thats returns an array.
var one = document.querySelector('.one');
var two = document.querySelector('.two');
If you want to set it as last children, then:
while(two.children.length>0)
one.appendChild(two.children[0]);
If you want it as first children, then move the content before the first child of one:
while(two.children.length>0)
one.insertBefore(two.children[0], one.firstChild);
And last, useful for similar cases a clean replacement for one could be with:
one.innerHTML = two.innerHTML;
Note: this last implementation removes all content of "one" and sets the content by "two"

Get all items that start with class name

I'm trying to only show certain divs. The way I have decided to do this is to first hide all elements that start with "page" and then only show the correct divs. Here's my (simplified) code:
<form>
<input type="text" onfocus="showfields(1);">
<input type="text" onfocus="showfields(2);">
</form>
<div class="page1 row">Some content</div>
<div class="page1 row">Some content</div>
<div class="page2 row">Some content</div>
<div class="page2 row">Some content</div>
<script>
function showfields(page){
//hide all items that have a class starting with page*
var patt1 = /^page/;
var items = document.getElementsByClassName(patt1);
console.log(items);
for(var i = 0; i < items.length; i++){
items[i].style.display = "none";
}
//now show all items that have class 'page'+page
var item = document.getElementsByClassName('page' + page);
item.style.display = '';
}
</script>
When I console.log(items); I get a blank array. I'm pretty sure the regexp is right (get all items starting with 'page').
The code I'm using is old school JS, but I'm not adverse to using jQuery. Also if there is a solution that doesn't use regexp, that's fine too as I'm new to using regexp's.
getElementsByClassName only matches on classes, not bits of classes. You can't pass a regular expression to it (well, you can, but it will be type converted to a string, which is unhelpful).
The best approach is to use multiple classes…
<div class="page page1">
i.e. This div is a page, it is also a page1.
Then you can simply document.getElementsByClassName('page').
Failing that, you can look to querySelector and a substring matching attribute selector:
document.querySelectorAll("[class^=page]")
… but that will only work if pageSomething is the first listed class name in the class attribute.
document.querySelectorAll("[class*=page]")
… but that will match class attributes which mention "page" and not just those with classes which start with "page" (i.e. it will match class="not-page".
That said, you could use the last approach and then loop over .classList to confirm if the element should match.
var potentials = document.querySelectorAll("[class*=page]");
console.log(potentials.length);
elementLoop:
for (var i = 0; i < potentials.length; i++) {
var potential = potentials[i];
console.log(potential);
classLoop:
for (var j = 0; j < potential.classList.length; j++) {
if (potential.classList[j].match(/^page/)) {
console.log("yes");
potential.style.background = "green";
continue elementLoop;
}
}
console.log("no");
potential.style.background = "red";
}
<div class="page">Yes</div>
<div class="notpage">No</div>
<div class="some page">Yes</div>
<div class="pageXXX">Yes</div>
<div class="page1">Yes</div>
<div class="some">Unmatched entirely</div>
Previous answers contain parts of the correct one, but none really gives it.
To do this, you need to combine two selectors in a single query, using the comma , separator.
The first part would be [class^="page"], which will find all the elements whose class attribute begins with page, this selector is thus not viable for elements with multiple classes, but this can be fixed by [class*=" page"] which will find all the elements whose class attribute have somewhere the string " page" (note the space at the beginning).
By combining both selectors, we have our classStartsWith selector:
document.querySelectorAll('[class^="page"],[class*=" page"]')
.forEach(el => el.style.backgroundColor = "green");
<div class="page">Yes</div>
<div class="notpage">No</div>
<div class="some page">Yes</div>
<div class="pageXXX">Yes</div>
<div class="page1">Yes</div>
<div class="some">Unmatched entirely</div>
You can use jQuery solution..
var $divs = $('div[class^="page"]');
This will get all the divs which start with classname page
$(document).ready(function () {
$("[class^=page]").show();
$("[class^=page]").hide();
});
Use this to show hide div's with specific css class it will show/hide all div's with css class mention.

count of dynamically created DIVs returning zero

Following is my code and relevant HTML , what i wanna do is that i wanna count the number of search-img-box within search-img-ctrl but i get 0 as output, just to tell here that
following div search-img-box is dynamically created.
jQuery(document).ready(function() {
var numberOfDivs = jQuery('#search-img-ctrl').filter('.search-img-box').length;
alert(numberOfDivs);
});
following is my HTML
<div id="search-img-ctrl" class="search-img-ctrl">
<div id="search-img-box" class="search-img-box" name="search-img-box">
<img width="335" height="206" src="" alt="">
<ul>
</div>
<div id="search-img-box" class="search-img-box" name="search-img-box">
</div> </div>
Here's a fiddle: http://jsfiddle.net/t5jcT/
I changed filter to find and got rid of the duplicate ids in the html.
jQuery(document).ready(function() {
var numberOfDivs = jQuery('#search-img-ctrl').find('.search-img-box').length;
alert(numberOfDivs);
});
or you can use selectors instead of the find as others have pointed out:
jQuery(document).ready(function() {
var numberOfDivs = jQuery('#search-img-ctrl .search-img-box').length;
alert(numberOfDivs);
});
use .find instead of .filter:
var numberOfDivs = jQuery('#search-img-ctrl').find('.search-img-box').length;
alert(numberOfDivs);
If you want to only find the number of direct children with that class you can use .children
var numberOfDivs = jQuery('#search-img-ctrl').children('.search-img-box').length;
Also make sure you edit your html so that your html elements don't have duplicate IDs

Categories