Is there a way to convert: $("#first").find("input").not("td td input")
(http://jsfiddle.net/4K9TG/)
...into a querySelectorAll selector?
(continuing from jQuery: find() children until a certain threshold element is encountered)
not is simply a filter over a collection that satisfies a condition. See if this helps.
var query = function(selector, element) {
element = element || document;
return [].slice.call(element.querySelectorAll(selector));
};
var not = function(selector) {
var exclude = query(selector);
return function(element) {
return exclude.indexOf(element) == -1;
};
};
var inputs = query('#first input').filter(not('td td input'));
Here's a demo: http://jsbin.com/EJAyiSux/1/edit
You can implement most jQuery methods as simple filter and map sequences with higher-order functions, as shown above. After all, jQuery collections are just arrays of elements.
I ended up making a function to do this.
http://jsfiddle.net/4K9TG/2/:
var n = get_elements_until (document.getElementById('first'), 'input', 'td')
console.log (n)
function get_elements_until (parent, tagname_to_search_for, tagname_to_stop_at) {
var element_list = []
var stack_current = [parent]
while (true) {
var stack_new = []
for (var s = 0, curlen_s = stack_current.length; s < curlen_s; s++) {
var children = stack_current[s].childNodes
for (var i = 0, curlen = children.length; i < curlen; i++) {
var child = children[i], tagname = child.tagName
if (typeof tagname == "undefined") continue
tagname = tagname.toLowerCase ()
if (tagname == tagname_to_search_for) element_list.push (child)
if (tagname != tagname_to_stop_at) stack_new.push (child)
}
}
stack_current = stack_new
if (stack_new.length == 0) break
}
return element_list
}
Related
I was trying to make a function that gives you the selected CSS properties of an element those you want. But it's pretty laggy if used in console as of it needs to get and match all CSS properties.
function styleOf(elementUseSelectors, propertiesToCheck, tellInConsole) {
var element = elementUseSelectors;
var Arguments = propertiesToCheck;
var calculatedProperties = [];
var matchedProperties = [];
if (tellInConsole !== undefined && tellInConsole == true) {
console.warn("Running styleOf() Please Don't Do Other Calculations This Function Disables Console.")
}
for (var i = 0; i < Object.keys(getComputedStyle(element)).length; i++) {
var value = getComputedStyle(element).getPropertyValue(Object.entries(getComputedStyle(element))[i][0].replace(/([A-Z])/g, ' $1').trim().replaceAll(" ", "-").toLowerCase());
if (value !== "") {
calculatedProperties.push(Object.entries(getComputedStyle(element))[i][0].replace(/([A-Z])/g, ' $1').trim().replaceAll(" ", "-").toLowerCase() + ": " + value);
}
}
for (var i = 0; i < calculatedProperties.length; i++) {
for (var a = 0; a < Arguments.length; a++) {
if (calculatedProperties[i].includes(Arguments[a])) {
window.splitted = calculatedProperties[i].split("");
window.joinThis = [];
for (var k = 0; k < splitted.indexOf(":"); k++) {
joinThis.push(splitted[k]);
};
if (joinThis.join("") == Arguments[a]) {
matchedProperties.push(calculatedProperties[i]);
}
}
}
}
if (tellInConsole !== undefined && tellInConsole == true) {
console.warn("StyleOf() Calculations Completed You Can Now Use Console.")
}
return matchedProperties
}
The TreeWalker object is designed to quickly parse DOM nodes in a document. If you expand on the example given above in the MDN Web Docs you can output the computed CSS properties for a given node.
The first property of the method is the node you want to traverse – in this case it's document.body:
var treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT,
{ acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } },
false
);
var nodeList = [];
var currentNode = treeWalker.currentNode;
while(currentNode) {
nodeList.push(currentNode);
const style = getComputedStyle(currentNode)
console.log(style)
currentNode = treeWalker.nextNode();
console.log("moving to next node...");
}
Welp #kaiido answered the question.
function styleOf(element, properties) {
const computed = getComputedStyle(element);
return properties.map( key => key + ": " + computed[ key ] )};
var style = styleOf(document.getElementsByTagName("body")[0], ["height", "width", "background-color", "font-size", "color", "font-family"]);
console.log(style);
I had made this function, and this is new yet, and I don't really know how to handle this roughly.
var $;
(function() {
$ = function(e) {
return new query(e);
};
var query = function(e) {
var e = document.querySelectorAll(e), i;
for (i = 0; i < e.length; i++) {
this[i] = e[i];
}
this.length = e.length;
return this;
};
$.fn.prototype = {
hide: function() {
var i;
for (i = 0; i < this.length; i++) {
this[i].style.display = 'block';
}
return this;
},
hasClass: function (klass) {
var e = this, i;
var t = [];
for (i = 0; i < e.length; i++) {
var k = e[i].className;
var array = k.split(' ');
// If the element has the class, add it for return
if (array.indexOf(klass) > -1) {
t.push(e[i]);
}
}
// Return the list of matched elements
return t;
}
}
} ());
window.onload = function() {
$(".element").hasClass("someClass").hide();
}
So yeah, that's the code above. I think I have matched the class, but what the problem is, It's not returning the elements. I'm new to prototyping so please don't be harsh. I really need to fix this one. Please don't tell me to go and have jquery. I don't want to use that massive library just because I want some css selectors.
this inside the function isn't the current element.
hasClass: function (klass) {
var e = this, i;
var t = [];
for (i = 0; i < e.length; i++) {
var k = e[i].className;
var array = k.split(' ');
// If the element has the class, add it for return
if (array.indexOf(klass) > -1) {
t.push(e[i]);
}
}
// Return the list of matched elements
return t;
}
and that is why your script do not return the element.
ALSO:
You tried to use jQuery's method to add your own functions in library, but actually this will not work, as you're writing pure js, not jquery, and you need to do many thing to make it work like jQuery.
$.fn.prototype = {
hide: function() {
var i;
for (i = 0; i < this.length; i++) {
this[i].style.display = 'block';
}
return this;
},
hasClass: function (klass) {
var e = this, i;
var t = [];
for (i = 0; i < e.length; i++) {
var k = e[i].className;
var array = k.split(' ');
// If the element has the class, add it for return
if (array.indexOf(klass) > -1) {
t.push(e[i]);
}
}
// Return the list of matched elements
return t;
}
}
}
The problem here is this line: $.fn.prototype = {};. There is no $.fn in your code anywhere, so you cannot set a prototype property on it. (In jQuery, $.fn is set to jQuery.prototype, that's why you can set $.fn.someMethod.)
If you want the hasClass method to exists on your objects, you need to modify the prototype of query. Your $ function returns new query objects, so query is where the hasClass method should be.
Also, your hasClass method returns an array. Arrays do not have a method called hide. You need to have your hasClass method return a query object, so that you can continue to chain methods.
P.S. Shouldn't a hide method set the style.display to 'none'?
Anyway, your code should look like this:
var $;
(function() {
$ = function(e) {
return new query(e);
};
var query = function(e) {
var e = Array.isArray(e) ? e : document.querySelectorAll(e),
i;
for (i = 0; i < e.length; i++) {
this[i] = e[i];
}
this.length = e.length;
return this;
};
query.prototype = {
hide: function() {
var i;
for (i = 0; i < this.length; i++) {
this[i].style.display = 'none';
}
return this;
},
hasClass: function(klass) {
var e = this,
i;
var t = [];
for (i = 0; i < e.length; i++) {
var k = e[i].className;
var array = k.split(' ');
// If the element has the class, add it for return
if (array.indexOf(klass) > -1) {
t.push(e[i]);
}
}
// Return the list of matched elements
return new query(t);
}
}
}());
window.onload = function() {
$(".element").hasClass("someClass").hide();
}
<p class="someClass element">You can't see me!</p>
(Note: Array.isArray doesn't work in IE < 9.)
If all you want is to query for elements matching a condition and hide it based on element type, id or class name CSS query selectors along with querySelector() or querySelectorAll() api.
For your code sample the same can be rewritten as below
var elements = document.querySelectorAll("element.someClass");
for(var i = 0; i <elements.length; i++) {
elements[i].style.visibility = 'hidden';
}
I have a JavaScript function that I want to fire once the user enters text inside an input element. Currently I can only see the function firing if I console.log it. How do I get it to fire using keyup method?
The relevant code is below.
var $ = function (selector) {
var elements = [],
i,
len,
cur_col,
element,
par,
fns;
if(selector.indexOf('#') > 0) {
selector = selector.split('#');
selector = '#' + selector[selector.length -1];
}
selector = selector.split(' ');
fns = {
id: function (sel) {
return document.getElementById(sel);
},
get : function(c_or_e, sel, par) {
var i = 0, len, arr = [], get_what = (c_or_e === 'class') ? "getElementsByClassName" : "getElementsByTagName";
if (par.length) {
while(par[I]) {
var temp = par[i++][get_what](sel);
Array.prototype.push.apply(arr, Array.prototype.slice.call(temp));
}
} else {
arr = par[get_what](sel);
}
return (arr.length === 1)? arr[0] : arr;
}
};
len = selector.length;
curr_col = document;
for ( i = 0; i < len; i++) {
element = selector[i];
par = curr_col;
if( element.indexOf('#') === 0) {
curr_col = fns.id(element.split('#'[1]));
} else if (element.indexOf('.') > -1) {
element = element.split('.');
if (element[0]) {
par = fns.get('elements', element[0], par);
for ( i =0; par[i]; i++) {
if(par[i].className.indexOf(element[1]> -1)) {
elements.push(par[i]);
}
}
curr_col = elements;
} else {
curr_col = fns.get('class', element[1], par);
}
} else {
curr_col = fns.get('elements', element, par);
}
}
return elements;
};
You need to bind your method to the keyup event on the page.
You could try
document.addEventListener('keyup', $)
Or assuming you have the input element as element you could do
element.addEventListener('keyup', $)
Your function will be passed the event which you could use to investigate the state of the element if you needed that information to trigger or not trigger things in the function.
Here's a quick sample where the function that get's run on keypress is changeColor.
var COLORS = ['red', 'blue','yellow', 'black']
var NCOLORS = COLORS.length;
function changeColor(ev) {
var div = document.getElementById('colored');
var colorIdx = parseInt(Math.random() * NCOLORS);
console.log(colorIdx);
var newColor = COLORS[colorIdx];
div.style.color = newColor
console.log("New color ", newColor)
}
document.body.addEventListener('keyup', changeColor)
Though I'm not using the event (ev), I like to show, in the code, that I expect that variable to be available.
See it in action here - http://codepen.io/bunnymatic/pen/yyLGXg
As a sidenote, you might be careful about calling your function $. Several frameworks (like jQuery) use that symbol and you may run into conflicts where you're overriding the global variable $ or where the framework overrides your version if it.
I'm looking for a better way to generate a map of parent-child-relations; based on specific id-pattern.
Its faster to ask for void 0 === cache[parent][child]; the expected result:
{
uuid_1: {uuid_2: {}}
uuid_2: {uuid_3: {}, uuid_4: {}}
uuid_3: {}
uuid_4: {}
}
The HTML structure:
<html id="uuid-1">
<body id="uuid-2">
<somewhere>
<whatever id="uuid-3" />
</somewhere>
<foo id="uuid-4" />
</body>
</html>
_fetch():
<1> // register as init
<2> // register as child of 1
<3>
<4 /> // register as child of 2
</3>
<5 /> // register as child of 2
</2>
</1>
Parse ~1300 elements (large menu structure) to find my ~50 uuids.
Try 1 with jQuery:
_fetch: function(element, factoryName)
{
var a = {}, l = 0, t = this, f = function(el, n)
{
if(!a[n]) a[n] = {};
var e = $(el), test = $('[id^="uuid-"]', e);
if(!test.length)
return;
e.children().each(function()
{
var u = $(this), id = u.attr('id'), q;
// anonymous element: no class defined
if(!(id && 'uuid-' === id.slice(0x00, 0x05)))
{
f(this, n); // continue with current name
return;
}
l++;
q = $.T.util.uuidFromId(id);
$.T.__dict[q] = '#' + id;
a[n][q] = {};
// comment in/out
f(this, q);
});
}
f(element, factoryName);
return a;
}
Try 2 with yellow JS:
..., g = function(n, p)
{
var r = [];
for(var d = (p || document).getElementsByTagName('*'), i = 0, l = d.length; i < l; i++)
d[i].getAttribute(n) && r.push(d[i]);
return r;
},
f = function(el, n)
{
var z = el.children.length, y = 0;
if(!a[n]) a[n] = {};
if(z && g('id', el)) for(; y < z; y++)
{
var u = el.children[y], id = u.getAttribute('id'), q;
if(!(id && 'uuid-' === id.slice(0x00, 0x05)))
{
f(u, n);
continue;
}
l++;
$.T.__dict[q = $.T.util.uuidFromId(id)] = '#' + id;
a[n][q] = {};
// it's irrelevant to fetch the full html or a sequence by constructor
//f(u, q);
}
}
My question is:
How to collect DOM elements as flat representation in a faster way; like the mapping above? My current solution is very laggy.
OT:
contextual x-dialog based on map:
<baz><alice><bob><bobchild/></bob></alice><foo />
alice._init:
before init children of bob
tell foo 'go away'
before init bob // context: no bob
after init children of alice // && alice without children
after init baz // && baz not ready -> no hello
tell baz 'hello'
I am still not quite sure I know what you're trying to do, but here's the fastest way I know to walk a DOM tree and accumulate parent/child info like you're doing to build the data structure you indicated you wanted to end up with:
var treeWalkFast = (function() {
// create closure for constants
var skipTags = {"SCRIPT": true, "IFRAME": true, "OBJECT": true,
"EMBED": true, "STYLE": true, "LINK": true, "META": true};
return function(parent, fn, allNodes) {
var parents = [];
var uuidParents = [];
parents.push(parent);
uuidParents.push(parent);
var node = parent.firstChild, nextNode, lastParent;
while (node && node != parent) {
if (allNodes || node.nodeType === 1) {
if (fn(node, parents, uuidParents) === false) {
return(false);
}
}
// if it's an element &&
// has children &&
// has a tagname && is not in the skipTags list
// then, we can enumerate children
if (node.nodeType === 1 && node.firstChild && !(node.tagName && skipTags[node.tagName])) {
// going down one level, add this item to the parent array
parents.push(node);
if (node.id && node.id.substr(0, 5) === "uuid-") {
uuidParents.push(node);
}
node = node.firstChild;
} else if (node.nextSibling) {
// node had no children so going to next sibling
node = node.nextSibling;
} else {
// no child and no nextsibling
// find parent that has a nextSibling
while ((node = node.parentNode) != parent) {
lastParent = parents.pop();
if (lastParent === uuidParents[uuidParents.length - 1]) {
uuidParents.pop();
}
if (node.nextSibling) {
node = node.nextSibling;
break;
}
}
}
}
}
})();
var objects = {uuid_1: {}};
treeWalkFast(document.documentElement, function(node, parents, uuidParents) {
if (node.id && node.id.substr(0, 5) === "uuid-") {
var uuidParent = uuidParents[uuidParents.length - 1];
if (!objects[uuidParent.id]) {
objects[uuidParent.id] = {};
}
objects[uuidParent.id][node.id] = {};
objects[node.id] = {};
}
});
Working demo here: http://jsfiddle.net/jfriend00/yzaJ6/
This is an adaptation of the treeWalkFast() function I wrote for this answer.
I am trying to implement my own getElementById() function in Javascript. My idea/algorithm goes like this:
function myGetElemById(id){
// rootNode I suppose will be the BODY tag.
rootElem = get elements by TAGNAME (rootNode);
elems = rootElems.getChildren();
for(i=0; i<elems.length; i++){
if(!elems[i].hasChildren()){
myGetElemById(elems[i]);
} else {
if(elems[i].id == id)
return elems[i];
else
return null;
}
}
}
Method 1:
function myGetElemById(id){
return document.getElementById(id);
}
Method 2:
function myGetElemById(id){
return window[id];
}
Method 3: (newer browsers)
function myGetElemById(id){
return document.querySelectorAll('#' + id);
}
DONE!
Okay, seriously:
function getById(id, parent, list){
parent = parent || document.body;
list = list || [];
var l, child, children = parent.children;
if(children){
l = children.length;
while(l--){
child = children[l];
if(child.id == id) list.push(child);
getById(id, child, list);
}
}
return list;
}
Check out this feature and maybe you can get ideas
function getElementsStartsWithId( id ) {
var children = document.body.getElementsByTagName('*');
var elements = [], child;
for (var i = 0, length = children.length; i < length; i++) {
child = children[i];
if (child.id.substr(0, id.length) == id)
elements.push(child);
}
return elements;
}
first, you must deal with the elements has children, call myGetElemById() and choose to return or not to return, depend on the result. like this
...
if(!elems[i].hasChildren()){
var result = myGetElemById(elems[i]);
if (result != null)
return result;
} else {
...
second why iterate over all the elements of the dom? the native function is much more faster.
Custom get element by ID method using BFS:
function myGetElementById(id, root){
let queue = []; // Using array here but linkedList is more performant in both time and space complexity
queue.push(root);
let currentNode;
while(queue.length){
currentNode = queue.shift();
if(currentNode.id === id){
return currentNode;
}
queue.push(...currentNode.children);
}
return false;
}