Remove all attributes from an HTML element and all its children [duplicate] - javascript

This question already has answers here:
Remove all attributes
(10 answers)
Closed 6 years ago.
In the following code, I would like to remove all attributes, classes, etc. from all the HTML tags inside the elements that have class "card-back", so the result are the bare tags (+ content) only.
I looked here and here, but couldn't get it to work.
Here's my code so far:
$.fn.removeAttributes = function(only, except) {
if (only) {
only = $.map(only, function(item) {
return item.toString().toLowerCase();
});
};
if (except) {
except = $.map(except, function(item) {
return item.toString().toLowerCase();
});
if (only) {
only = $.grep(only, function(item, index) {
return $.inArray(item, except) == -1;
});
};
};
return this.each(function() {
var attributes;
if (!only) {
attributes = $.map(this.attributes, function(item) {
return item.name.toString().toLowerCase();
});
if (except) {
attributes = $.grep(attributes, function(item, index) {
return $.inArray(item, except) == -1;
});
};
} else {
attributes = only;
}
var handle = $(this);
$.each(attributes, function(index, item) {
handle.removeAttr(item);
});
});
};
$('.card_back').removeAttributes(null, null).filter(function() {
var data = $(this);
back = data.html().trim();
alert(back);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="card_wrapper">
<div class="card_navigation">
zurück |
<a title="Titletext" href="/xyz">next</a> </div>
<div class="card_front">
<span class="info">Front</span>
<p>here's just some text
<br>and one more line.
</p>
<p>here's just another text
<br>and one more line.
</p>
</div>
<div class="card_back">
<span class="info">Back</span>
<p class="test"><span id="test3">Lorem Ipsum non dolor <strong>nihil est major</strong>, laudat amemus hibitet</span></p>
<p><span style="color: red">- <strong>Non solum</strong>, sed calucat ebalitant medetur</span></p>
<p> </p>
</div>
</div>

As pointed out in this response you can extend removeAttr to take no parameters and delete all attributes.
BEWARE, YOU WILL REMOVE SRC ATTRIBUTE FROM IMAGES INSIDE!!!
Then paired with removeClass (wich already can take no params) and a loop over each element gives this:
var removeAttr = jQuery.fn.removeAttr;
jQuery.fn.removeAttr = function() {
if (!arguments.length) {
this.each(function() {
// Looping attributes array in reverse direction
// to avoid skipping items due to the changing length
// when removing them on every iteration.
for (var i = this.attributes.length -1; i >= 0 ; i--) {
jQuery(this).removeAttr(this.attributes[i].name);
}
});
return this;
}
return removeAttr.apply(this, arguments);
};
$('.card_back').find('*').each(function( index, element ) {
$(element).removeClass();
$(element).removeAttr();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="card_wrapper">
<div class="card_navigation">
zurück |
<a title="Titletext" href="/xyz">next</a> </div>
<div class="card_front">
<span class="info">Front</span>
<p>here's just some text
<br>and one more line.
</p>
<p>here's just another text
<br>and one more line.
</p>
</div>
<div class="card_back">
<span class="info">Back</span>
<p class="test"><span id="test3">Lorem Ipsum non dolor <strong>nihil est major</strong>, laudat amemus hibitet</span></p>
<p><span style="color: red">- <strong>Non solum</strong>, sed calucat ebalitant medetur</span></p>
<p> </p>
</div>
</div>

Related

How to get all elements that can display text on the page?

I want to write a script to replace all text meeting a condition with something else.
However, I don't want it to replace text in elements such as script, style, etc. which are not shown/rendered.
What is the best way to distinguish these elements?
//Example of idea:
var elements = document.getElementsByTagName("*");
var element;
var text;
for(var i=0; i<elements.length; i++){
element = elements[i];
//Need to detect only text that is displayed.
text = element.textContent;
if(checkText(text)){element.textContent = somethingElse;}//Abstract idea
}
You could try this
$(':contains("targetText")').text("newText");
This is the purpose of TreeWalkers and the document.createTreeWalker method:
function getTextNodes (root) {
var tw = document.createTreeWalker(root || document.body, NodeFilter.SHOW_TEXT, {
acceptNode: function(node) {
return /^(STYLE|SCRIPT)$/.test(node.parentElement.tagName) ||
/^\s*$/.test(node.data) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT
}
})
var result = []
while (tw.nextNode()) result.push(tw.currentNode)
return result
}
var textNodes = getTextNodes()
// Text nodes before
console.log(
textNodes.map(function (n) { return n.data })
)
// Example text data transformation
textNodes.forEach(function (n) {
n.data = n.data.toUpperCase()
})
// Text nodes after
console.log(
textNodes.map(function (n) { return n.data })
)
<p>Lorem ipsum dot dolor sit amet...</p>
<span>More example text!</span>
<style>
.omitted style { }
</style>
<script>
'omitted script'
</script>
You can also use jQuery's star selector (*) and say :not(script) to the tags you want to ignore.
The following will replace any element that has the content "Test" with "Replaced", while ignoring any <script> and <style> tags.
$("body *:not(script):not(style)").each(function() {
if ($(this).text() == "Test") {
$(this).text("Replaced");
}
});
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
<div class="content">
<h1>Hello World</h1>
<p>Lorem ipsum...</p>
<p>Test</p>
<ul>
<li>Hello</li>
<li>World</li>
<li>Test</li>
</ul>
</div>

Creating parent and child elements wit Js/jQuery

I'm beginner at js/jquery.
I want to code this structure with js/jquery:
<div class="box">
<p>1</p>
<div class="content"><span>Lorem</span></div>
</div>
<div class="box">
<p>2</p>
<div class="content"><span>Ipsum</span></div>
</div>
<div class="box">
<p>3</p>
<div class="content"><span>Dolor</span></div>
</div>
<div class="box">
<p>4</p>
<div class="content"><span>Sit</span></div>
</div>
<div class="box">
<p>5</p>
<div class="content"><span>Amet</span></div>
</div>
I have this code:
function addDivs(n) {
for(var i=1; i<=n; i++) {
var parentP = $("<p>"+i+"</p>");
var parentContent = $("<div class='content'></div>");
var boxClassDiv = document.createElement("div");
document.body.appendChild(boxClassDiv);
boxClassDiv.setAttribute("class", "box");
$("body").prepend(boxClassDiv, [parentP, parentContent]);
}
}
window.onload = function getFuncs() {
addDivs(16);
}
Here is fiddle: https://jsfiddle.net/ds6wj38k/2/
I found a few similar questions like this and tried to add to my code but i can't adjust.
Thanks.
So first of all you need a div with class box:
var box = $('<div>').addClass('box');
Then you want a p with a number:
var p = $('<p>').text(1);
And finally a div with class content and span inside:
var content = $('<div>').addClass('content');
var span = $('<span>').text('any');
content.append(span);
So you created elements you need. Time to combine them:
var listItem = box.append(p).append(content);
And add to body!
$('body').append(listItem);
The final code:
function addDivs(n) {
for (var i = 1; i <= n; i++) {
var box = $('<div>').addClass('box');
var p = $('<p>').text(i);
var content = $('<div>').addClass('content');
var span = $('<span>').text('any');
content.append(span);
var listItem = box.append(p).append(content);
$('body').append(listItem);
}
}
window.onload = function getFuncs() {
addDivs(16);
}
Check out code online: http://jsbin.com/xeyugubefu/edit?js,output
Here is how I would suggest to do it:
function addDivs(words) {
words.forEach( function (word, i) {
$('body')
.append($('<div>').addClass('box')
.append($('<p>').text(i+1))
.append($("<div>").addClass('content').append($('<span>').text(words[i]))));
});
};
$(function getFuncs() {
var words = 'Lorem ipsum dolor sit amet'.split(' ');
addDivs(words);
});
jQuery is designed to support method chaining, which the above code demonstrates.
Note that apart from building the content with jQuery, you should also replace window.onload with a jQuery.ready call, which you can write as $(callback).

Javascript object property doesn't return correct length

I dynamically clone an element using jQuery and ask an object to return its length, but it returns 1 every time. Please see fiddle:
https://jsfiddle.net/gatzkerob/x5xd2x7q/3/
<span class="count">0</span>
<br>
<button>click</button>
<br>
<span class="a">a</span>
var obj = {
elem : $('.a')
}
function cloneThisAndCount(){
$('.a').last().after($('.a').first().clone());
$('.count').text(obj.elem.length);
}
$('button').click(function(){
cloneThisAndCount();
});
var obj = {
elem : $('.a')
}
will be computed once and cached into obj.elem in the begenning. which will be 1.
Note (thanx to nnnnn): When you have a reference to a jQuery object it is possible that other code could update that object to add more elements to it.
SOLUTION 1:
What you need is to do is, redo the selector every time before calculating length.
function cloneThisAndCount() {
$('.a').last().after($('.a').first().clone());
$('.count').text($('.a').length);
}
$('button').click(function() {
cloneThisAndCount();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="count">0</span>
<br>
<button>click</button>
<br>
<span class="a">a</span>
SOLUTION 2:
Change obj to:
var obj = {
elem : function(){ return $('.a')}
}
and then check length like: $('.count').text(obj.elem().length);
var obj = {
elem: function() {
return $('.a')
}
}
function cloneThisAndCount() {
$('.a').last().after($('.a').first().clone());
$('.count').text(obj.elem().length);
}
$('button').click(function() {
cloneThisAndCount();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="count">0</span>
<br>
<button>click</button>
<br>
<span class="a">a</span>

Javascript search and display divs with matching keywords

What I'm looking for:
I'm working on creating an easy way for a user to search a list of people, and for results to instantly display below the search field. The results MUST display "close" results, rather than exact. For example: User searches for "Mr. Smith" and The following existing result is displayed: "John Smith" (since there is no "Mr. Smith" entry, it displayed one with the keyword "smith")
What I have:
I have a working code that lets the user enter some characters and all divs that include a string matching the input is displayed (see in jsfiddle: http://jsfiddle.net/891nvajb/5/ Code is also below)
Unfortunately, this only displays results that match EXACTLY.
<body>
<input type="text" id="edit_search" onkeyup="javascript: find_my_div();">
<input type="button" onClick="javascript: find_my_div();" value="Find">
<script>
function gid(a_id) {
return document.getElementById (a_id) ;
}
function close_all(){
for (i=0;i<999; i++) {
var o = gid("user_"+i);
if (o) {
o.style.display = "none";
}
}
}
function find_my_div(){
close_all();
var o_edit = gid("edit_search");
var str_needle = edit_search.value;
str_needle = str_needle.toUpperCase();
if (str_needle != "") {
for (i=0;i<999; i++) {
var o = gid("user_"+i);
if (o) {
var str_haystack = o.innerHTML.toUpperCase();
if (str_haystack.indexOf(str_needle) ==-1) {
// not found, do nothing
}
else{
o.style.display = "block";
}
}
}
}
}
</script>
<div id="user_0" style="display:none">Andy Daulton<br/>Owner Nissan<br/><br/></div>
<div id="user_1" style="display:none">Doug Guy<br/>Bug Collector<br/><br/></div>
<div id="user_2" style="display:none">Sam Hilton<br/>Famous Celebrity in Hollywood<br/><br/></div>
<div id="user_3" style="display:none">Don Grey<br/>Old man<br/><br/></div>
<div id="user_4" style="display:none">Amy Hinterly<br/>Cook<br/><br/></div>
<div id="user_5" style="display:none">Gary Doll<br/>Racecar Driver<br/><br/></div>
<div id="user_6" style="display:none">Tod Akers<br/>Football Player<br/><br/></div>
<div id="user_7" style="display:none">Greg Barkley<br/>Interior designer<br/><br/></div>
<div id="user_8" style="display:none">Alen Simmons<br/>8th place winner<br/><br/></div>
Split the words in the search string with a regex like
searchString.split(/\W/);
and do a OR search over each of the words in the resulting array.
Updated fiddle
var searchStrings = str_needle.split(/\W/);
for (var i = 0, len = searchStrings.length; i < len; i++) {
var currentSearch = searchStrings[i].toUpperCase();
if (currentSearch !== "") {
nameDivs = document.getElementsByClassName("name");
for (var j = 0, divsLen = nameDivs.length; j < divsLen; j++) {
if (nameDivs[j].textContent.toUpperCase().indexOf(currentSearch) !== -1) {
nameDivs[j].style.display = "block";
}
}
}
}
One further approach, is as follows:
function gid(a_id) {
return document.getElementById(a_id);
}
function close_all() {
// applies the Array.prototype.forEach() method to the array-like nodeList
// returned by document.querySelectorAll() (the string passed to which finds all
// elements with an id that starts with ('^=') the string 'user_':
[].forEach.call(document.querySelectorAll('[id^=user_]'), function(div) {
// 'div' is the array element (the node) itself:
div.style.display = 'none';
});
}
function find_my_div() {
close_all();
// getting the trimmed lower-cased string from the input element, split
// on white-space characters to create an array:
var keywords = gid('edit_search').value.trim().toLowerCase().split(/\s+/),
// as above, selecting all elements whose id starts with the string 'user_':
haystack = document.querySelectorAll('[id^="user_"]'),
// working out whether text is accessed by node.textContent, or node.innerText:
textProp = 'textContent' in document.body ? 'textContent' : 'innerText',
// an initialised variable, for later:
userWords,
// filters the haystack (the divs whose id starts with 'user_'):
found = [].filter.call(haystack, function(user) {
// assigns the lower-cased string to the created-variable:
userWords = user[textProp].toLowerCase();
// returns those div elements whose text contains some of
// the words returned, earlier, as the keywords:
return keywords.some(function (word) {
return userWords.indexOf(word) > -1;
});
});
// iterates over the found elements, and shows them:
[].forEach.call(found, function(user) {
user.style.display = 'block';
});
}
<body>
<input type="text" id="edit_search" onkeyup="javascript: find_my_div();">
<input type="button" onClick="javascript: find_my_div();" value="Find">
<script>
function gid(a_id) {
return document.getElementById(a_id);
}
function close_all() {
[].forEach.call(document.querySelectorAll('[id^=user_]'), function(div) {
div.style.display = 'none';
});
}
function find_my_div() {
close_all();
var keywords = gid('edit_search').value.trim().toLowerCase().split(/\s+/),
haystack = document.querySelectorAll('[id^="user_"]'),
textProp = 'textContent' in document.body ? 'textContent' : 'innerText',
userWords,
found = [].filter.call(haystack, function(user) {
userWords = user[textProp].toLowerCase();
return keywords.some(function (word) {
return userWords.indexOf(word) > -1;
});
});
console.log(found);
[].forEach.call(found, function(user) {
user.style.display = 'block';
});
}
</script>
<div id="user_0" style="display:none">Andy Daulton
<br/>Owner Nissan
<br/>
<br/>
</div>
<div id="user_1" style="display:none">Doug Guy
<br/>Bug Collector
<br/>
<br/>
</div>
<div id="user_2" style="display:none">Sam Hilton
<br/>Famous Celebrity in Hollywood
<br/>
<br/>
</div>
<div id="user_3" style="display:none">Don Grey
<br/>Old man
<br/>
<br/>
</div>
<div id="user_4" style="display:none">Amy Hinterly
<br/>Cook
<br/>
<br/>
</div>
<div id="user_5" style="display:none">Gary Doll
<br/>Racecar Driver
<br/>
<br/>
</div>
<div id="user_6" style="display:none">Tod Akers
<br/>Football Player
<br/>
<br/>
</div>
<div id="user_7" style="display:none">Greg Barkley
<br/>Interior designer
<br/>
<br/>
</div>
<div id="user_8" style="display:none">Alen Simmons
<br/>8th place winner
<br/>
<br/>
</div>
References:
CSS:
Attribute-presence and value selectors.
JavaScript:
Array.prototype.every().
Array.prototype.filter().
Array.prototype.forEach().
Array.prototype.some().
document.querySelectorAll().
Function.prototype.call().
String.prototype.indexOf()

How do you test for equivalence between two HTML elements or jQuery selectors?

Can you write a function that sees if two pieces of DOM are equivalent? It should ignore irrelevant whitespace. Ideally, it should also ignore empty attributes.
So the following should be equal:
<div style="">
<div>a</div>
</div>
and
<div><div>a</div></div>
I've created a function that will compare the lengths of the jQuery objects and compare the tag names of each child element. So far it seems to work fairly well in the basic fiddle I've created for it. Here is the code, let me know how it works for you and if there are any bugs/improvements needed to be made. This does not compare the inner text though, however if you need that I think it should be easy enough to tweak.
UPDATE 1:
Changed to use recursion instead of a for loop, and it now compares the inner text of each element as well. If you don't need the text checked just delete that comparison.
UPDATE 2:
Per the OP comment, I've found and edited a few extra functions that can be utilized to compare CSS of the elements and check that they are the same. If not then the element are not the same and will return false.
jQuery:
This first function is a jQuery function that will return an object of the CSS properties.
(function ($) {
$.fn.getStyleObject = function () {
var dom = this.get(0);
var style;
var returns = {};
if (window.getComputedStyle) {
var camelize = function (a, b) {
return b.toUpperCase();
}
style = window.getComputedStyle(dom, null);
for (var i = 0; i < style.length; i++) {
var prop = style[i];
var camel = prop.replace(/\-([a-z])/g, camelize);
var val = style.getPropertyValue(prop);
returns[camel] = val;
}
return returns;
}
if (dom.currentStyle) {
style = dom.currentStyle;
for (var prop in style) {
returns[prop] = style[prop];
}
return returns;
}
return this.css();
}
})(jQuery);
Next function defined is used to compare the objects returned by the previous function.
function arrays_equal(a, b) {
var result = true;
$.each(a, function (key, value) {
if (!b.hasOwnProperty(key) || b[key] !== a[key]) {
result = false;
}
});
return result;
}
This is the primary function that checks for equality between the two elements. It will make a call to the added jQuery function to retrieve an object made of the CSS properties and then compares the two objects the second function that was added.
function equalElements(a, b) {
if (a.length != b.length) {
return false;
}
if (a.children().length != b.children().length) {
return false;
} else {
if (a.children().length > 0) {
var x = a.children().first();
var y = b.children().first();
equalElements(x, y);
}
if (a.get(0).tagName != b.get(0).tagName) {
return false;
}
if (a.text() != b.text()) {
return false;
}
var c = a.getStyleObject();
var d = b.getStyleObject();
if (!arrays_equal(c, d)) {
return false;
}
}
return true
}
HTML:
(Used to compare and test function)
<div id="div1">
<div>a</div>
</div>
<div id="div2" style="">
<div>a</div>
</div>
<div id="div3" style=""></div>
<div id="div4" style="">
<div>a</div>
<div>a</div>
</div>
<div id="div5" style="">
<div>b</div>
</div>
<div id="div6" style="width: 50px;">
<div>a</div>
</div>
<div id="div7" style="width: 50px;">
<div>a</div>
</div>
<div id="div8" style="width: 25px;">
<div>a</div>
</div>
<div id="div9" style="width: 50px; height: 10px;">
<div>a</div>
</div>
<ul id="list1">
<li>a</li>
</ul>
Test Code:
(Sample see fiddle for full test)
var a = $("#div1");
var b = $("#div2");
var same = equalElements(a, b);
if (same) {
alert("div1 is equal to div2");
}

Categories

Resources