$(selector, element) Native JS alternative - javascript

Hi I'm trying to remove all jQuery from my platform one line at a time.
But I'm having some trouble finding a replacement for this
$('[data-attribute="value"]', GenericHTMLElement);
I was hoping it would be something simple like
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value"></span><span data-something-else="1000"></span></div>';
var b = div.childNodes;
var a = b.querySelector('[data-attribute="value"]');
But that's not working either. Does have any suggestions for me?

As commented,
childNodes will give you a list of elements. This list will not have querySelector. If you loop over nodes, you should be able to get it though. But, my suggestion is just do div.querySelector(...)
To be specific, it will be of type NodeList. This is a collection of nodes. So you cannot run querySelector on it. You can either loop over all nodes and do querySelector on them or just so this operation on parent div.
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value">Dummy Text</span><span data-something-else="1000"></span></div>';
var b = div.childNodes;
console.log('Type of childNodes is: ', Object.prototype.toString.call(b))
// getting element using loop over childNodes
for(var i = 0; i<b.length; i++) {
var el = b[i].querySelector('[data-attribute="value"]');
el && console.log(el.textContent)
}
// getting element using parent elenent.
var el1 = div.querySelector('[data-attribute="value"]');
console.log(el1.textContent)

First you need to understand what the first code does. It searches for given selector, limiting it to HTMLElementObject scope. Understanding that we can try to do something similar.
From MSDN example, he is using body element:
var el = document.body.querySelector("style[type='text/css'], style:not([type])");
They have this example with data-attributes, take a look please.

The reason your attempt isn't working is that you're trying to call querySelector on a NodeList, which doesn't have a querySelector method.
If you try it on a single element, it works fine:
function mySelect(selector, el) {
return el.querySelector(selector);
}
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value"></span><span data-something-else="1000"></span></div>';
var b = div.childNodes[0];
console.log(mySelect('[data-attribute="value"]', b));
But this makes it so that mySelect(selector, el) is nothing more than an alias for el.querySelector(selector).
Presumably, you'd want to be able to evaluate a selector on multiple elements at once, and return multiple results, like jQuery does. In that case, you can do so by making some adjustments:
function flatMap(values, f) {
return Array.prototype.concat.apply([], values.map(f));
}
function mySelect(selector, els) {
return flatMap(els.length ? Array.from(els) : [els], function (el) {
return Array.from(el.querySelectorAll(selector));
});
}
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value">span 1</span><span data-something-else="1000"></span></div><div><span data-attribute="value">span 2</span></div>';
console.log(mySelect('[data-attribute="value"]', div.childNodes));
console.log(mySelect('[data-attribute="value"]', div.childNodes[0]));

Related

object HTMLDivElement to string [duplicate]

Imagine I have the following HTML:
<div><span><b>This is in bold</b></span></div>
I want to get the HTML for the div, including the div itself. Element.innerHTML only returns:
<span>...</span>
Any ideas? Thanks
Use outerHTML:
var el = document.getElementById( 'foo' );
alert( el.outerHTML );
Expanding on jldupont's answer, you could create a wrapping element on the fly:
var target = document.getElementById('myElement');
var wrap = document.createElement('div');
wrap.appendChild(target.cloneNode(true));
alert(wrap.innerHTML);
I am cloning the element to avoid having to remove and reinsert the element in the actual document. This might be expensive if the element you wish to print has a very large tree below it, though.
First, put on element that wraps the div in question, put an id attribute on the element and then use getElementById on it: once you've got the lement, just do 'e.innerHTML` to retrieve the HTML.
<div><span><b>This is in bold</b></span></div>
=>
<div id="wrap"><div><span><b>This is in bold</b></span></div></div>
and then:
var e=document.getElementById("wrap");
var content=e.innerHTML;
Note that outerHTML is not cross-browser compatible.
old question but for newcomers that come around :
document.querySelector('div').outerHTML
You'll want something like this for it to be cross browser.
function OuterHTML(element) {
var container = document.createElement("div");
container.appendChild(element.cloneNode(true));
return container.innerHTML;
}
If you want a lighter footprint, but a longer script, get the elements innerHTML and only create and clone the empty parent-
function getHTML(who,lines){
if(!who || !who.tagName) return '';
var txt, ax, str, el= document.createElement('div');
el.appendChild(who.cloneNode(false));
txt= el.innerHTML;
ax= txt.indexOf('>')+1;
str= txt.substring(0, ax)+who.innerHTML+ txt.substring(ax);
el= null;
return lines? str.replace(/> *</g,'>\n<'): str;
//easier to read if elements are separated
}
var x = $('#container').get(0).outerHTML;
as outerHTML is IE only, use this function:
function getOuterHtml(node) {
var parent = node.parentNode;
var element = document.createElement(parent.tagName);
element.appendChild(node);
var html = element.innerHTML;
parent.appendChild(node);
return html;
}
creates a bogus empty element of the type parent and uses innerHTML on it and then reattaches the element back into the normal dom
define function outerHTML based on support for element.outerHTML:
var temp_container = document.createElement("div"); // empty div not added to DOM
if (temp_container.outerHTML){
var outerHTML = function(el){return el.outerHTML||el.nodeValue} // e.g. textnodes do not have outerHTML
} else { // when .outerHTML is not supported
var outerHTML = function(el){
var clone = el.cloneNode(true);
temp_container.appendChild(clone);
outerhtml = temp_container.innerHTML;
temp_container.removeChild(clone);
return outerhtml;
};
};
var el = document.getElementById('foo');
el.parentNode.innerHTML;

jQuery append element if it doesn't exist, otherwise replace

Here's a short piece of code:
var $el = $("#something").find(".test");
if (!$el.length) {
$("#something").append('<div class="test">somecontent</div>');
} else {
$el.replaceWith('<div class="test">somenewcontent</div>');
}
I couldn't find a method appendOrReplaceWith or anything similar.
Any ideas how can I make it shorter?
I believe that:
$("#something").appendOrReplace('<div class="test">sometext</div>');
would be much easier to read, but no such method is available yet.
Just remove it first then append.
$(".test").remove();
$("#something").append('<div class="test">somecontent</div>');
Mandatory vanilla answer. It may not be shorter, but it's faster.
Get the element, grab all subelements with the class "test", create your div, check the subelements length, and if length is truthy, set the innerHTML to the div. Else, append it.
var el = document.getElementById("something");
var subel = el.getElementsByClassName("test");
var div = document.createElement("div");
div.className = "test"
if (subel.length) {
div.textContent = "somenewcontent";
while(el.hasChildNodes()) el.removeChild(el.lastChild); //remove child nodes
el.appendChild(div);
} else {
div.textContent = "somecontent";
el.appendChild(div);
}
Adding a method like findOrAppend to jQuery could be useful:
$.fn.findOrAppend = function(selector, content) {
var elements = this.find(selector);
return elements.length ? elements : $(content).appendTo(this);
}
Then you can chain text, replaceWith, empty etc. as needed:
$("#something")
.findOrAppend(".test", "<div class='test'>")
.text("newcontent");
First of all you should cache your selectors:
var $som = $('#something');
var $ele = $(".test",$som);
var newHtml = '<div class="test">somecontent</div>';
if (!$el[0]) $som.append( newHtml );
else $ele.replaceWith( newHtml );
but you already did it really fine, (beside not caching repeated selectors), and me, trying to make it smaller could be a**-kicked for not using {} for my if and else :)
I would do this
var $s = $("#something"), $t = $s.find(".test"), c = 'New content';
( $t[0] ? $t:$s)[( $t[0] ? 'html':'append')](( $t[0] ? c :$('<div>',{class:'test'}).append(c)));

Javascript regex to replace text div and < >

var text='<div id="main"><div class="replace">< **My Text** ></div><div>Test</div></div>'
I want to replace div with class="replace" and html entities < > comes inside that div with some other text.
I.e the output :
'<div id="main"> Hello **My Text** Hello <div>Test</div> </div>'
I've tried
var div = new RegExp('<[//]{0,1}(div|DIV)[^><]*>', 'g');
text = text.replace(div, "Hello");
but this will replace all div.
Any help gratefully received!
If a Jquery solution is acceptable:
text = $(text) // Convert HTML string to Jquery object
.wrap("<div />") // Wrap in a container element to make...
.parent() // the whole element searchable
.find("div.replace") // Find <div class="replace" />
.each(function() // Iterate over each div.replace
{
$(this)
.replaceWith($(this).html() // Replace div with content
.replace("<", "<sometext>")
.replace(">", "</sometext>")); // Replace text
})
.end().html(); // return html of $(text)
This sets text to:
<div id="main"><sometext> My Text </sometext><div>Test</div></div>
And to replace it back again:
text = text.replace('<sometext>', '<div class="replace"><')
.replace('</sometext>', '></div>');
http://api.jquery.com/jquery/#jQuery2
http://api.jquery.com/each/
http://api.jquery.com/find/
http://api.jquery.com/html/
In pure JS it will be something like this:
var elements = document.getElementsByClassName('replace');
var replaceTag = document.createElement('replacetext');
for (var i = elements.length - 1; i >= 0; i--) {
var e = elements[i];
e.parentNode.replaceChild(replaceTag, e);
};​
Here is one crazy regex which matches what you want:
var text='<div id="main"><div class="replace">< **My Text** ></div><div>Test</div></div>'
var r = /(<(div|DIV)\s+class\s*?=('|")\s*?replace('|")\s*?>)(\s*?<)(.*?)(>\s*?)(<\/(div|DIV)\s*?>)/g;
The whole replacement can be made with:
text.replace(r, function () {
return 'Hello' + arguments[6] + 'Hello';
});
Please let me know if there are issues with the solution :).
Btw: I'm totally against regexes like the one in the answer...If you have made it with that complex regex there's probably better way to handle the problem...
Consider using the DOM instead; you already have the structure you want, so swap out the node itself (borrowing heavily from #maxwell's code, but moving children around as well):
var elements = document.getElementsByClassName('replace');
for(var i = elements.length-1; i>= 0; --i) {
var element = elements[i];
var newElement = document.createElement('replacetext');
var children = element.childNodes;
for(var ch = 0; ch < children.length; ++i) {
var child = children[ch];
element.removeChild(child);
newElement.appendChild(child);
}
element.parentNode.insertBefore(newElement,element);
element.parentNode.removeChild(element);
}
For each element of the given class, then, it will move each of its children over to the new element before using that element's position to insert the new element and finally removing itself.
My only questionmark is whether the modification of items in the array return by getElementByClassName will cause problems; it might need an extra check to see if the element is valid before processing it, or you may prefer to write this as a recursive function and process the tree from deepest node first.
It may seem like more work, but this should be faster (no re-parsing of the html after you've changed it, element moves are just reference value assignments) and much more robust. Attempting to parsing HTML may damage your health.
Rereading the question (always a good plan), you begin with the text in a string. If that is truly the start point (i.e. you're not just pulling that out of an innerHTML value), then to use the above just create a temporary parent element:
var fosterer = document.createElement('div');
fosterer.innerHTML = text; // your variable from the question
And then proceed using fosterer.getElementsByClassName.

Get list elements and its classes using jquery

I used $('#ul li').get() to get all the list elements and stored in an array, each of this list elements have classes...
var i;
var listClass = ('#ul li').get();
for(i=0;i<listClass.length;i++){
var theClass = listClass[i].attr("class"); //<--what's the proper function/method/code for this?
var content = listClass[i].innerHTML; //<-- works very well
//other codes here
}
How may i able to get the classes of each list elements...Thanks!
You can use jQuery's own map to do that:
alert($('#ul li').map(function() {
return this.className;
}).get());
http://jsfiddle.net/MhVU7/
for example. You can do anything with the returned array.
The reason the way you're doing it isn't working is because you're calling the non-existent method .attr on a native DOM element - it's not an extended jQuery object.
var lis = document.getElementById("ul").children;
for (var i = 0, len = lis.length; i < len; i++) {
var li = lis[i],
className = li.className,
value = li.value,
text = li.textContent;
// code
}
The get() method returns a native array of DOM elements, not a jQuery object.
You should use jQuery:
var lists = $('ul li');
var className = lists.eq(i).attr('class');
var content = lists.eq(i).text();
If you want to loop through all the elements
$('ul li').each(function(){
var className = $(this).attr('class');
var content = $(this).text();
});
I have commented the code to better help you understand it.
$("#ul li").each(function() { /* you should only be using # selector to identify id's - if it's all ul's you want just put ul. */
var klass = this.className; /* this refers to the native DOM object, which contains className */
var textContents = this.innerText || this.textContent; /* the text of the list, does not include html tags */
var childNodes = this.childNodes; /* the child nodes of the list, each unencased string of text will be converted into a TextNode */
console.log(klass + ' ' + textContents); /* replace console.log with alert if you do not have a console */
console.log(childNodes);
});
here is an example of the above.
Good Luck!

How to correctly use innerHTML to create an element (with possible children) from a html string?

Note: I do NOT want to use any framework.
The goal is just to create a function that will return an element based on an HTML string.
Assume a simple HTML Document like such:
<html>
<head></head>
<body>
</body>
</html>
All functions mentioned are in included the head section and all DOM creation/manipulation is done at the end of the body in a script tag.
I have a function createElement that takes a well formed HTML String as an argument. It goes like this:
function createElement(str)
{
var div = document.createElement('div');
div.innerHTML = str;
return div.childNodes;
}
Now this functions works great when you call it like such:
var e = createElement('<p id="myId" class="myClass">myInnerHTML</p>');
With the minor (possibly HUGE) problem that the element created isn't a 'true' element, it still has a parentNode of 'div'. If anyone knows how to fix that, then that would be awesome.
Now if I call the same function with a more complex string:
var e = createElement('<p id="myId" class="myClass">innerHTML<h2 id="h2ID" class="h2CLASS">Heading2</h2></p>');
It creates TWO children instead of ONE child with another child having another child.Once you do div.innerHTML = str. The innerHTML instead of
`<p id="myId" class="myClass">innerHTML <h2 id="h2ID" class="h2CLASS">Heading2</h2> </p>`
turns to
`<p id="myId" class="myClass">innerHTML</p> <h2 id="h2ID" class="h2CLASS">Heading2</h2>`
Questions:
Can I somehow get an element without a parent node after using .innerHTML?
Can I (in the case of the slightly complex string) get my function to return ONE element with the appropriate child instead of two elements. [It actually returns three, <p.myClass#myId>,<h2.h2CLASS#h2ID>, and another <p>]
This is similar to the answer from palswim, except that it doesn't bother with creating a clone, and uses a while() loop instead, always appending the node at [0].
function createElement( str ) {
var frag = document.createDocumentFragment();
var elem = document.createElement('div');
elem.innerHTML = str;
while (elem.childNodes[0]) {
frag.appendChild(elem.childNodes[0]);
}
return frag;
}
You'd have to attach the new element somewhere. Try using a DocumentFragment object in conjunction with the div you created:
function createElement(str) {
var div = document.createElement('div');
div.innerHTML = str;
var container = document.createDocumentFragment();
for (var i=0; i < div.childNodes.length; i++) {
var node = div.childNodes[i].cloneNode(true);
container.appendChild(node);
}
return container.childNodes;
}
It's more overhead, but it does what you want. Note that DOM elements' .insertAdjacentHTML member function is coming in HTML5.
For that complex string you passed, it isn't valid XHTML syntax - you can't have a block element as a child of <p> (<h2> is a block level element).

Categories

Resources