Is there a way to pass 'missing' object to .append() - javascript

jQuery's .append() function can take multiple arguments, either flat or in an array. I have some code where I need to append 3 items, one of which might not exist, like:
whatever.append(always).append(maybe).append(alwaysToo);
/* or */
whatever.append(always, maybe, alwaysToo);
/* or */
var arrayOfThoseThree = [ always, maybe, alwaysToo ];
whatever.append(arrayOfThoseThree);
I can not make out from the jQuery docs what, if anything, the value of maybe should be to say "just ignore this one":
maybe = '';
maybe = null;
maybe = undefined;
maybe = ???
as in:
maybe = needMaybe ? $('<blah...>') : ignoreThisValue;
I could, of course, do something like:
whatever.append(always);
if (maybe) whatever.append(maybe);
whatever.append(alwaysToo);
but that's ugly (especially as this is part of a larger chain).
And I could experiment with different values until I find one that "works", but I was hoping there was an "official" documented way that won't fail to work some future day because I was using an "undocumented feature".
Point me in the right direction?
[EDIT]
I was wondering in general, but the concrete example in front of me is:
var titl = this.dataset.title; /* optional */
var ifr = $('<iframe>');
var bas = $('<base href="' + document.baseURI + '">');
var ttl = titl ? $('<title>' + titl + '</title>') : null; /* HERE */
var lnk = $('<link rel="stylesheet" href="/css/print.css">');
/* ... */
ifr.contents().find('head').append(bas, ttl, lnk);

How about
whatever.append([always, maybe, alwaysToo].filter(item => !!item));

Here's what happens in the jQuery code (the version I'm using anyway).
Note that this defines what "works" today, not what is documented to work and continue working in the future.
The .append() function is written similarly to many others in that domManip() does much of the work:
append: function() {
return this.domManip( arguments, function( elem ) {
if ( this.nodeType === 1 ||
this.nodeType === 11 ||
this.nodeType === 9 ) {
var target = manipulationTarget( this, elem );
target.appendChild( elem );
}
});
},
and the first thing domManip() does is:
domManip: function( args, callback ) {
// Flatten any nested arrays
args = concat.apply( [], args );
then it calls buildFragment():
fragment = jQuery.buildFragment( args, ... );
which does:
buildFragment: function( elems, context, scripts, selection ) {
var /* ..., */ i = 0;
for ( ; i < l; i++ ) {
elem = elems[ i ];
if ( elem || elem === 0 ) {
/* ... process this argument ... */
}
}
So empty arrays get squashed by Array.prototype.concat() and then anything that fails the test ( elem || elem === 0 ) gets ignored.
So, in fact, when ttl could be null, all of these (currently) do "the right thing":
whatever.append( bas, ttl, lnk);
whatever.append([bas, ttl, lnk]);
whatever.append([bas],[ttl], [lnk]);
whatever.append( bas, [ttl], lnk);
whatever.append(bas).append( ttl ).append(lnk);
whatever.append(bas).append([ttl]).append(lnk);
But, as near as I can find, the documentation makes no statements about a value or values which you can use which will safely be ignored (now and forever).
Thus the safest course of action (at least where => is supported) is the Answer from Assan:
whatever.append( [bas, ttl, lnk].filter( e => !!e ) );

Related

Is there a simple way to quickly pass the entire contents of a given array into an `Array.prototype` method variable without using parameters?

context
I created an array docket to keep track of coordinates as the user clicks on a canvas space. During the main program loop the array is to be scanned by a draw function so that selected pixels can be seen. Originally, inside of my event listener, I was using the push( ) method but then I realized I wanted a way to sort of toggle the pixels.
code description
So I added a method poke( ) to Array.prototype, as seen below, which allows me to push the whole docket array into a local array param.array and assign the trigger coordinate to a local variable param.entry. entry is then pushed into array and array is processed by the main poke( ) loop to ensure there are no duplicate values. If a match is found, both elements are annihilated and param.array is returned to the top, ultimately shrinking docket by 1; If no matches are found then no elements are annihilated and param.array is returned to the top, ultimately expanding docket by 1.
main issue: example 1
Anyway, as the method is currently written, it must be called thusly:
docket.poke( docket, e.key ); Note: for simplicity I have used keyboard key values.
Array.prototype.poke = function( a, b ) {
var bool = { }, i = { }, param = { };
param.array = a; param.entry = b;
//
param.array.push( param.entry );
i.len = param.array.length;
i.end = i.len - 1;
//
for ( i.cur = 0; i.cur < i.len; i.cur++ ) {
bool.match = param.array[ i.cur ] == param.array[ i.end ];
bool.nSelf = !( i.cur == i.end );
//
if ( bool.match && bool.nSelf ) {
param.array.splice( i.end, 1 );
param.array.splice( i.cur, 1 );
//
i.end -= 2;
i.len -= 2;
}
}
//
return param.array;
}
This seems a little redundant, but it offers two critical advantages. First to readability and aesthetic. Being able to visibly pass off the contents of docket to a local array for processing and then visibly return the results to the top I think is very helpful to comprehension. Second, both this example and the next use a sort of confusing truth test to filter out false positives on duplicate value detection. This example doesn't have too though. It could easily be rewritten to compare each element in param.array to param.entry using a tight, no nonsense for loop.
main issue: example 2
docket.poke( e.key ); is the less redundant and more desired approach. This is my code.
Array.prototype.poke = function( a ) {
var bool = { }, entry = a, i = { };
//
this.push( entry );
i.len = this.length;
i.end = i.len - 1;
//
for ( i.cur = 0; i.cur < i.len; i.cur++ ) {
bool.match = this[ i.cur ] == this[ i.end ];
bool.nSelf = !( i.cur == i.end );
//
if ( bool.match && bool.nSelf ) {
this.splice( i.end, 1 );
this.splice( i.cur, 1 );
//
i.end -= 2;
i.len -= 2;
}
}
}
As you can see, this eliminates the the redundancy in the call, but it sacrifices some readability of the method and more importantly the opportunity to really slim up the code using the simple comparison I mentioned above.
So now I'm wondering if there is some less than obvious way that I've missed which will allow me to pass the full contents of my array to a local variable without having to first pass them in as a parameter of its own method.
Any ideas?
There is no reason to define the method on the prototype if you are going to pass the array as an argument. A plain function would be just fine for that.
The second version of your code has indeed the advantage that you can apply the method to a given array instead of passing the array to a function.
The code could however be simplified if:
You would only add the element after you have determined it does not yet occur in the array
You would use indexOf:
Array.prototype.toggle = function(value) {
var index = this.indexOf(value);
if (index > -1) {
this.splice(index, 1);
} else {
this.push(value);
}
}
var a = [4,2,5,8];
a.toggle(2);
console.log(a.join());
a.toggle(2);
console.log(a.join());
NB: I personally find the name toggle more telling than poke.
Consider also the power of a Set: it will find an existing member in constant time (while an array implementation needs linear time), and will also be able to remove it in constant time. So if you are open to using something else than an array for this, go for a Set.
Set.prototype.toggle = function(value) {
if (!this.delete(value)) this.add(value);
}
var a = new Set([4,2,5,8]);
a.toggle(2);
console.log([...a].join());
a.toggle(2);
console.log([...a].join());

Get the matches from specific groups

I want to validate a String and, after that, get all the matches from some groups.
RegEx:
/^<[A-Za-z0-9]>::=(<[A-Za-z0-9]>)+(\|(<[A-Za-z0-9]>)+)+$/
So, if I get something like <A>::=<B><A>|<Y><A>|<Z> is valid, but if I get something like <A>::=<B>| is false.
There's no problem with the validation, the problem is that I want to take the text inside < and > because I need it later.
So, if I get <exparit>::=<number>|<number><exparit>, then I want to get ["exparit", "number", "number", "exparit"]
My code looks like
Rules = {
"BNF" : /^<[A-Za-z0-9]>::=(<[A-Za-z0-9]>)+(\|(<[A-Za-z0-9]>)+)+$/
};
var checkBNF = function ( bnf ) {
if ( Rules.BNF.test( bnf ) ) {
console.log('ok');
//How to get the text inside < and > ??
}
else {
console.log('no');
}
};
I really appreciate any kind of help, such a book, link, example or the resolution of this problem.
Thanks!
If I can help you a bit, here's something to stat with :
http://jsfiddle.net/URLhb/1/
var test = "<A>::=<B><A>|<Y><A>|<Z>";
var patt0 = /<([a-zA-Z]*)>/g; //Old and bad version
var patt1 = /[a-zA-Z]+/g;
var testRE = test.match(patt1);
alert(testRE[0]);
alert(testRE[1]);
alert(testRE[2]);
alert(testRE[3]);
alert(testRE[4]);
alert(testRE[5]);
This code will capture the text inside the < >, but also the < and > symbols with it. I'm trying to fix this, I'll update if I get a better result.
EDIT : Found the issue : I was using a * instead of a + ! It works perfectly now !
What I did was slightly different as I wasn't sure that you would only ever have [a-zA-Z0-9] within the <>
Rules = {
"BNF" : /^<[A-Za-z0-9]>::=(<[A-Za-z0-9]>)+(\|(<[A-Za-z0-9]>)+)+$/
};
var checkBNF = function ( bnf ) {
if ( Rules.BNF.test( bnf ) ) {
console.log('ok');
//How to get the text inside < and > ??
var patt = /\<(.*?)\>/g;
var strArray = bnf.match(patt);
for (i=0;i<strArray.length;i++) {
strArray[i] = strArray[i].replace('<','').replace('>','');
}
return strArray;
}
else {
console.log('no');
}
};
var test = "<A>::=<B><A>|<Y><A>|<Z>";
var result = checkBNF(test);
console.log(result)
http://jsfiddle.net/UmT3P/

What's the meaning about the parameter seed

I'm reading Sizzle source code. I saw the definition below
function Sizzle(selector, context, results, seed)
My question is what's the meaning about the parameter seed? I can't find it in API document
Thanks
addendum
The seed parameter is used in jQuery's event handler source (from 2.1.4):
jQuery.find = Sizzle;
// [...]
jQuery.event = {
// [..]
handlers: function( event, handlers ) {
// [..]
// Find delegate handlers
if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
for ( ; cur !== this; cur = cur.parentNode || this ) {
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
if ( cur.disabled !== true || event.type !== "click" ) {
matches = [];
for ( i = 0; i < delegateCount; i++ ) {
handleObj = handlers[ i ];
// Don't conflict with Object.prototype properties (#13203)
sel = handleObj.selector + " ";
if ( matches[ sel ] === undefined ) {
matches[ sel ] = handleObj.needsContext ?
jQuery( sel, this ).index( cur ) >= 0 :
//
// Right here to find if cur matches the
// delegated event handler's selector.
//
jQuery.find( sel, this, null, [ cur ] ).length;
// There: -----------------------^
}
if ( matches[ sel ] ) {
matches.push( handleObj );
}
}
if ( matches.length ) {
handlerQueue.push({ elem: cur, handlers: matches });
}
}
}
}
},
You can use the seed parameter to limit the selection to a list of candidates. Just pass in an array of DOM elements.
For example let's say we have the following DOM:
<div id="id1"></div>
<div id="id2"></div>
Then, perform the following selections:
Sizzle("#id1", null, null, null);
// [<div id=​"id1">​</div>​]
And:
var candidates = [
document.getElementById("id1"),
document.getElementById("id2")
];
Sizzle("#id1", null, null, candidates);
// [<div id=​"id1">​</div>​]
But:
var candidates = [
document.getElementById("id2")
];
Sizzle("#id1", null, null, candidates);
// []
Note: This functionality doesn't seem to be part of the public API.
A seed is usually used to determine specific sequences of pseudo-random numbers. If you want the same repeated order of numbers on every run you use the same seed. Random number generators can use time stamps to make sure seeds vary, but for testing it it extremely useful to be able to set such seeds.
I assume the seed in this case will have a similar meaning, it will mean the outcome of Sizzle will be identical on every run if the seed is the same, if it is different the outcomes will be different.

Get border width from a div with plain javascript

I got this style applied to a div
div#content {
border: 1px solid skyblue;
}
and i want to be able to alert the width of the border, I have tried with this:
window.alert( document.getElementById( "content" ).style.borderWidth );
I heard that depends of the browser maybe you can help me
I'm using Firefox 18
Please try the below javascript:
alert($("#content").css("border-left-width")); //using jquery.
or
alert(getComputedStyle(document.getElementById('content'),null).getPropertyValue('border-left-width'));//without jquery.
getComputedStyle(element, pseudo)
element:The element to get a styling for
pseudo:A pseudo-selector like ‘hover’ or null if not needed.
Reference link: http://javascript.info/tutorial/styles-and-classes-getcomputedstyle
I might be too late but as you never marked it as answered, I thought I could give it a try.
If your problem was compatibility between browser I would create a custom method that I could use in almost every browser there is (that means going back to the very basics).
I actually dug a lot to do this. I use some of the code from jQuery because I did not want to use jQuery but still have the backwards compatibility that jQuery does.
This function solves your question and at the bottom there are some examples on how to use it.
This functions uses the "module pattern" through the immediate function that will be run as soon as the script loads creating a method that will NOT polute the global scope but extend its functionality through a function to do what you wanted.
// I give it a name but it can also be anonymous
(function preloadedFunctions(){
// Preseted methods.
if(window.getComputedStyle){
window.getComputedStylePropertyValue = function(element, prop){
var computedStyle = window.getComputedStyle(element, null);
if(!computedStyle) return null;
if(computedStyle.getPropertyValue) {
return computedStyle.getPropertyValue(prop);
} else if (computedStyle.getAttribute) {
return computedStyle.getAttribute(prop);
} else if(computedStyle[prop]) {
return computedStyle[prop];
};
};
}
// jQuery JavaScript Library v1.9.0
// http://www.minhacienda.gov.co/portal/pls/portal/PORTAL.wwsbr_imt_services.GenericView?p_docname=6240612.JS&p_type=DOC&p_viewservice=VAHWSTH&p_searchstring=
// For IE8 or less
else if ( document.documentElement.currentStyle ) {
var rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
rposition = /^(top|right|bottom|left)$/,
core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source;
window.getComputedStylePropertyValue = function(element, prop){
var left, rsLeft,
ret = element.currentStyle && element.currentStyle[ prop ],
style = element.style;
if ( ret == null && style && style[ prop ] ) {
ret = style[ prop ];
}
if ( rnumnonpx.test( ret ) && !rposition.test( prop ) ) {
left = style.left;
rsLeft = element.runtimeStyle && element.runtimeStyle.left;
if ( rsLeft ) {
element.runtimeStyle.left = element.currentStyle.left;
}
style.left = prop === "fontSize" ? "1em" : ret;
ret = style.pixelLeft + "px";
style.left = left;
if ( rsLeft ) {
element.runtimeStyle.left = rsLeft;
}
}
return ret === "" ? "auto" : ret;
};
};
})();
i.e.
1.-
var borderWidth = getComputedStylePropertyValue(document.getElementsByTagName("div")[0], "border-width");
console.log(borderWidth);
2.-
var div = document.getElementById("someID");
console.log(getComputedStylePropertyValue(div, "border-width"));
If somebody is still looking, this seems to be easiest way to do it with plain JS.
let border =
+getComputedStyle((document.getElementById("idOfYourElement")))
.borderTopWidth.slice(0, -2)
Explanation below:
document.getElementById("idOfYourElement") - Return our HTML element.
getComputedStyle - Return css attributes of chosen element as object.
.borderTopWidth - Corresponding attribute from getComputedStyle object (return array like this: ("10px")).
.slice(0, -2) - Cut the last 2 characters from our array so we get rid of px at the end.
And + at the start - Parse rest of our string, that contains number we want, to the integer.
You can try this:
var border = document.getElementById("yourDiv").clientWidth - document.getElementById("yourDiv").offsetWidth;
alert(border);
Note, that the value will be rounded to an integer. If fractional value is required, you need to use getComputedStyle instead (see other answers).
Very old question, but anyway...
This solution is plain JavaScript, and should work in older browsers too.
It measures the size of the element, with, and without borders.
The following example should work correctly if the borders around the element are all the same size.
If not, the procedure doesn't change much, you just have to set the borders equal to zero, one by one.
var ele=document.getElementById("content");
// measures the element width, WITH borders
var w=ele.offsetWidth;
var cssBackup=ele.style.cssText;
// sets the borders to zero
ele.style.border="0px";
// computes the border size
var bord=(w-ele.offsetWidth)/2;
// resets the css
ele.style.cssText=cssBackup;
alert(bord);
When left & right border has same width:
function getWidth(div) {
return (div.offsetWidth - div.clientWidth) /2
}
getWidth(document.querySelector('#content'))
According to W3Schools, this property is supported by major browsers. Thus you shouldn't have any difficulty in using it.
However, using a JavaScript framework like jQuery would always help you not worrying about trivial issues like this.
Works for me
let content = document.querySelector('#content');
// here 'borderWidth' is similar to element.style syntax
let contentBorderWidth = getComputedStyle(content).borderWidth; // 1px
// number, without 'px'
let contentBorderWidthNumber = parseFloat(getComputedStyle(content).borderWidth); // 1
// demo
content.innerHTML = contentBorderWidth +', '+ contentBorderWidthNumber;
// in your case, to alert
window.alert(contentBorderWidth +', '+ contentBorderWidthNumber);
div#content {
border: 1px solid skyblue;
}
<div id="content"></div>
More about getComputedStyle.

Alternative to getElementsByTagName

Since the getElementsByTagName() function is new (DOM-1?) I wanted another more reliable method to get a reference to an element based on its tag name/id.
Edit- Without using a framework, since I need to cut down on size; so 10-20K for a framework is unacceptable. I just need the JS code that can fetch an element
getElementsByTagName is not new. It is supported since IE5, FF1 and Opera 7 according to w3schools
[edit]
Thanks for pointing this out. It was indeed supported since Opera 7.
As mentioned, getElementsByTagName is not new...
I think you're going to get about 10 references to jQuery.
Returns all the paragraph elements:
$('p').length
If 19kb is too big, and you just want to do element selection, something like sizzle works well, at about 4kb. The only thing I would note is that you're probably going to end up needing something that's in jQuery anyway.
http://sizzlejs.com/
Queries are very similar:
Sizzle("li");
19kb is a really small one-time price to pay for the power of jQuery.
If all you want to do is select elements, it may be smart to just use the sizzle selector engine and not a full blown library. I would go with the full library, but, going with a selector engine might be useful in limited circumstances.
Sizzle is the CSS selector engine that powers jQuery.
http://sizzlejs.com/
Or prototype, etc. You'll need to use one of these javascript glue libraries to achieve this. All of them will call this function if it exists, but fake it otherwise.
Here is an implementation based on the jQuery 1.12.4 implementation. It uses getElementsByTagName if available. If not, it uses querySelectorAll if available. If not, it falls back on recursively traversal. jQuery 1.12.4 supports older browsers, such as IE6, according to themselves.
function getElementsByTagName( node, tagName ) {
if (tagName == '*') {
tagName = undefined;
}
var merge = function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
while ( j < len ) {
first[ i++ ] = second[ j++ ];
}
// Support: IE<9
// Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
if ( len !== len ) {
while ( second[ j ] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
first.length = i;
return first;
},
nodeName = function( elem, name ) {
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
},
elems, elem,
i = 0,
context = node,
tag = tagName,
found = typeof context.getElementsByTagName !== "undefined" ?
context.getElementsByTagName( tag || "*" ) :
typeof context.querySelectorAll !== "undefined" ?
context.querySelectorAll( tag || "*" ) :
undefined;
if ( !found ) {
for ( found = [], elems = context.childNodes || context;
( elem = elems[ i ] ) != null;
i++
) {
if ( !tag || nodeName( elem, tag ) ) {
found.push( elem );
} else {
merge( found, getElementsByTagName( elem, tag ) );
}
}
}
return found;
/* return tag === undefined || tag && nodeName( context, tag ) ?
merge( [ context ], found ) :
found;*/
}
I took the getAll() internal function of jQuery 1.12.4 and copied in the two helper functions it needs (jQuery.nodeName and jQuery.merge). I also made sure you can call it with "*" as tagName by adding a few lines in the top of the function. Finally, at the end of the function I commented out some functionality, which adds current node to result (if it matches), and simply returns the found nodes.
Be aware that the function in some cases returns an HTMLCollection, and in other circumstances returns an Array. Also beware that when "*" is passed as tagname, output differs depending on browser: The Element.prototype.getElementsByTagName does not return TextNodes, but the recursive traversal does.
Alternatively, you could use picoQuery. picoQuery is an implementation of jQuery, where you can select which methods you need in an online builder. in this case, you need no methods, as selection is part of core, and the build is only 1kb gzipped. picoQuery is written for modern browsers, but falls back to jQuery 1.12.4 for older browsers.

Categories