Finding only visible elements using JavaScript - javascript

I'm working on coverting a JQuery project to pure JavaScript and I'm stuck with the following bit of code.
$(".element-has-class:visible")
I thought perhaps something along these lines might work to catch all visible element (in the case of my project list items) but I've had no luck:
function functionName (){
var elementsOnShow = document.getElementsByClassName('element-has-class').find(isVisible);
}
function isVisible(element) {
return element.style.display === 'block';
}
(block has been set in the CSS). Is there anyway to get all visible elements within one variable?

You can convert your nodeList to an Array (read more about it here), which will allow you to use Array.prototype.filter() to get the visible elements:
function functionName (){
var myNodeList = document.getElementsByClassName('element-has-class'),
myArray = [].slice.call(myNodeList),
elementsOnShow = myArray.filter(isVisible);
}
function isVisible(element) {
return element.offsetWidth > 0
|| element.offsetHeight > 0
|| element.getClientRects().length > 0;
}
The isVisible function you see above is extracted from jQuery 2.2.4's source code (version 3.X.X is not compatible with IE 8 and below).

Related

Modifying a string of html in javascript [duplicate]

I'm trying to get the HTML of a selected object with jQuery. I am aware of the .html() function; the issue is that I need the HTML including the selected object (a table row in this case, where .html() only returns the cells inside the row).
I've searched around and found a few very ‘hackish’ type methods of cloning an object, adding it to a newly created div, etc, etc, but this seems really dirty. Is there any better way, or does the new version of jQuery (1.4.2) offer any kind of outerHtml functionality?
I believe that currently (5/1/2012), all major browsers support the outerHTML function. It seems to me that this snippet is sufficient. I personally would choose to memorize this:
// Gives you the DOM element without the outside wrapper you want
$('.classSelector').html()
// Gives you the outside wrapper as well only for the first element
$('.classSelector')[0].outerHTML
// Gives you the outer HTML for all the selected elements
var html = '';
$('.classSelector').each(function () {
html += this.outerHTML;
});
//Or if you need a one liner for the previous code
$('.classSelector').get().map(function(v){return v.outerHTML}).join('');
EDIT: Basic support stats for element.outerHTML
Firefox (Gecko): 11 ....Released 2012-03-13
Chrome: 0.2 ...............Released 2008-09-02
Internet Explorer 4.0...Released 1997
Opera 7 ......................Released 2003-01-28
Safari 1.3 ...................Released 2006-01-12
No need to generate a function for it. Just do it like this:
$('a').each(function(){
var s = $(this).clone().wrap('<p>').parent().html();
console.log(s);
});
(Your browser's console will show what is logged, by the way. Most of the latest browsers since around 2009 have this feature.)
The magic is this on the end:
.clone().wrap('<p>').parent().html();
The clone means you're not actually disturbing the DOM. Run it without it and you'll see p tags inserted before/after all hyperlinks (in this example), which is undesirable. So, yes, use .clone().
The way it works is that it takes each a tag, makes a clone of it in RAM, wraps with p tags, gets the parent of it (meaning the p tag), and then gets the innerHTML property of it.
EDIT: Took advice and changed div tags to p tags because it's less typing and works the same.
2014 Edit : The question and this reply are from 2010. At the time, no better solution was widely available. Now, many of the other replies are better : Eric Hu's, or Re Capcha's for example.
This site seems to have a solution for you :
jQuery: outerHTML | Yelotofu
jQuery.fn.outerHTML = function(s) {
return s
? this.before(s).remove()
: jQuery("<p>").append(this.eq(0).clone()).html();
};
What about: prop('outerHTML')?
var outerHTML_text = $('#item-to-be-selected').prop('outerHTML');
And to set:
$('#item-to-be-selected').prop('outerHTML', outerHTML_text);
It worked for me.
PS: This is added in jQuery 1.6.
Extend jQuery:
(function($) {
$.fn.outerHTML = function() {
return $(this).clone().wrap('<div></div>').parent().html();
};
})(jQuery);
And use it like this: $("#myTableRow").outerHTML();
I agree with Arpan (Dec 13 '10 5:59).
His way of doing it is actually a MUCH better way of doing it, as you dont use clone. The clone method is very time consuming, if you have child elements, and nobody else seemed to care that IE actually HAVE the outerHTML attribute (yes IE actually have SOME useful tricks up its sleeve).
But I would probably create his script a bit different:
$.fn.outerHTML = function() {
var $t = $(this);
if ($t[0].outerHTML !== undefined) {
return $t[0].outerHTML;
} else {
var content = $t.wrap('<div/>').parent().html();
$t.unwrap();
return content;
}
};
To be truly jQuery-esque, you might want outerHTML() to be a getter and a setter and have its behaviour as similar to html() as possible:
$.fn.outerHTML = function (arg) {
var ret;
// If no items in the collection, return
if (!this.length)
return typeof arg == "undefined" ? this : null;
// Getter overload (no argument passed)
if (!arg) {
return this[0].outerHTML ||
(ret = this.wrap('<div>').parent().html(), this.unwrap(), ret);
}
// Setter overload
$.each(this, function (i, el) {
var fnRet,
pass = el,
inOrOut = el.outerHTML ? "outerHTML" : "innerHTML";
if (!el.outerHTML)
el = $(el).wrap('<div>').parent()[0];
if (jQuery.isFunction(arg)) {
if ((fnRet = arg.call(pass, i, el[inOrOut])) !== false)
el[inOrOut] = fnRet;
}
else
el[inOrOut] = arg;
if (!el.outerHTML)
$(el).children().unwrap();
});
return this;
}
Working demo: http://jsfiddle.net/AndyE/WLKAa/
This allows us to pass an argument to outerHTML, which can be
a cancellable function — function (index, oldOuterHTML) { } — where the return value will become the new HTML for the element (unless false is returned).
a string, which will be set in place of the HTML of each element.
For more information, see the jQuery docs for html().
You can also use get (Retrieve the DOM elements matched by the jQuery object.).
e.g:
$('div').get(0).outerHTML;//return "<div></div>"
As extension method :
jQuery.fn.outerHTML = function () {
return this.get().map(function (v) {
return v.outerHTML
}).join()
};
Or
jQuery.fn.outerHTML = function () {
return $.map(this.get(), function (v) {
return v.outerHTML
}).join()
};
Multiple choice and return the outer html of all matched elements.
$('input').outerHTML()
return:
'<input id="input1" type="text"><input id="input2" type="text">'
To make a FULL jQuery plugin as .outerHTML, add the following script to any js file and include after jQuery in your header:
update New version has better control as well as a more jQuery Selector friendly service! :)
;(function($) {
$.extend({
outerHTML: function() {
var $ele = arguments[0],
args = Array.prototype.slice.call(arguments, 1)
if ($ele && !($ele instanceof jQuery) && (typeof $ele == 'string' || $ele instanceof HTMLCollection || $ele instanceof Array)) $ele = $($ele);
if ($ele.length) {
if ($ele.length == 1) return $ele[0].outerHTML;
else return $.map($("div"), function(ele,i) { return ele.outerHTML; });
}
throw new Error("Invalid Selector");
}
})
$.fn.extend({
outerHTML: function() {
var args = [this];
if (arguments.length) for (x in arguments) args.push(arguments[x]);
return $.outerHTML.apply($, args);
}
});
})(jQuery);
This will allow you to not only get the outerHTML of one element, but even get an Array return of multiple elements at once! and can be used in both jQuery standard styles as such:
$.outerHTML($("#eleID")); // will return outerHTML of that element and is
// same as
$("#eleID").outerHTML();
// or
$.outerHTML("#eleID");
// or
$.outerHTML(document.getElementById("eleID"));
For multiple elements
$("#firstEle, .someElesByClassname, tag").outerHTML();
Snippet Examples:
console.log('$.outerHTML($("#eleID"))'+"\t", $.outerHTML($("#eleID")));
console.log('$("#eleID").outerHTML()'+"\t\t", $("#eleID").outerHTML());
console.log('$("#firstEle, .someElesByClassname, tag").outerHTML()'+"\t", $("#firstEle, .someElesByClassname, tag").outerHTML());
var checkThisOut = $("div").outerHTML();
console.log('var checkThisOut = $("div").outerHTML();'+"\t\t", checkThisOut);
$.each(checkThisOut, function(i, str){ $("div").eq(i).text("My outerHTML Was: " + str); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://rawgit.com/JDMcKinstry/ce699e82c7e07d02bae82e642fb4275f/raw/deabd0663adf0d12f389ddc03786468af4033ad2/jQuery.outerHTML.js"></script>
<div id="eleID">This will</div>
<div id="firstEle">be Replaced</div>
<div class="someElesByClassname">At RunTime</div>
<h3><tag>Open Console to see results</tag></h3>
you can also just do it this way
document.getElementById(id).outerHTML
where id is the id of the element that you are looking for
I used Jessica's solution (which was edited by Josh) to get outerHTML to work on Firefox. The problem however is that my code was breaking because her solution wrapped the element into a DIV. Adding one more line of code solved that problem.
The following code gives you the outerHTML leaving the DOM tree unchanged.
$jq.fn.outerHTML = function() {
if ($jq(this).attr('outerHTML'))
return $jq(this).attr('outerHTML');
else
{
var content = $jq(this).wrap('<div></div>').parent().html();
$jq(this).unwrap();
return content;
}
}
And use it like this: $("#myDiv").outerHTML();
Hope someone finds it useful!
// no cloning necessary
var x = $('#xxx').wrapAll('<div></div>').parent().html();
alert(x);
Fiddle here: http://jsfiddle.net/ezmilhouse/Mv76a/
If the scenario is appending a new row dynamically, you can use this:
var row = $(".myRow").last().clone();
$(".myRow").last().after(row);
.myrow is the classname of the <tr>. It makes a copy of the last row and inserts that as a new last row.
This also works in IE7, while the [0].outerHTML method does not allow assignments in ie7
node.cloneNode() hardly seems like a hack. You can clone the node and append it to any desired parent element, and also manipulate it by manipulating individual properties, rather than having to e.g. run regular expressions on it, or add it in to the DOM, then manipulate it afterwords.
That said, you could also iterate over the attributes of the element to construct an HTML string representation of it. It seems likely this is how any outerHTML function would be implemented were jQuery to add one.
I've used Volomike's solution updated by Jessica. Just added a check to see if the element exists, and made it return blank in case it doesn't.
jQuery.fn.outerHTML = function() {
return $(this).length > 0 ? $(this).clone().wrap('<div />').parent().html() : '';
};
Of course, use it like:
$('table#buttons').outerHTML();
You can find a good .outerHTML() option here https://github.com/darlesson/jquery-outerhtml.
Unlike .html() that returns only the element's HTML content, this version of .outerHTML() returns the selected element and its HTML content or replaces it as .replaceWith() method but with the difference that allows the replacing HTML to be inherit by the chaining.
Examples can also be seeing in the URL above.
This is quite simple with vanilla JavaScript...
document.querySelector('#selector')
Note that Josh's solution only works for a single element.
Arguably, "outer" HTML only really makes sense when you have a single element, but there are situations where it makes sense to take a list of HTML elements and turn them into markup.
Extending Josh's solution, this one will handle multiple elements:
(function($) {
$.fn.outerHTML = function() {
var $this = $(this);
if ($this.length>1)
return $.map($this, function(el){ return $(el).outerHTML(); }).join('');
return $this.clone().wrap('<div/>').parent().html();
}
})(jQuery);
Edit: another problem with Josh's solution fixed, see comment above.
Anothe similar solution with added remove() of the temporary DOM object.
I have made this simple test with outerHTML being tokimon solution (without clone), and outerHTML2 being jessica solution (clone)
console.time("outerHTML");
for(i=0;i<1000;i++)
{
var html = $("<span style='padding:50px; margin:50px; display:block'><input type='text' title='test' /></span>").outerHTML();
}
console.timeEnd("outerHTML");
console.time("outerHTML2");
for(i=0;i<1000;i++)
{
var html = $("<span style='padding:50px; margin:50px; display:block'><input type='text' title='test' /></span>").outerHTML2();
}
console.timeEnd("outerHTML2");
and the result in my chromium (Version 20.0.1132.57 (0)) browser was
outerHTML: 81ms
outerHTML2: 439ms
but if we use tokimon solution without the native outerHTML function (which is now supported in probably almost every browser)
we get
outerHTML: 594ms
outerHTML2: 332ms
and there are gonna be more loops and elements in real world examples, so the perfect combination would be
$.fn.outerHTML = function()
{
$t = $(this);
if( "outerHTML" in $t[0] ) return $t[0].outerHTML;
else return $t.clone().wrap('<p>').parent().html();
}
so clone method is actually faster than wrap/unwrap method
(jquery 1.7.2)
Here is a very optimized outerHTML plugin for jquery:
(http://jsperf.com/outerhtml-vs-jquery-clone-hack/5 => the 2 others fast code snippets are not compatible with some browsers like FF < 11)
(function($) {
var DIV = document.createElement("div"),
outerHTML;
if ('outerHTML' in DIV) {
outerHTML = function(node) {
return node.outerHTML;
};
} else {
outerHTML = function(node) {
var div = DIV.cloneNode();
div.appendChild(node.cloneNode(true));
return div.innerHTML;
};
}
$.fn.outerHTML = function() {
return this.length ? outerHTML(this[0]) : void(0);
};
})(jQuery);
#Andy E => I don't agree with you. outerHMTL doesn't need a getter AND a setter: jQuery already give us 'replaceWith'...
#mindplay => Why are you joining all outerHTML? jquery.html return only the HTML content of the FIRST element.
(Sorry, don't have enough reputation to write comments)
Short and sweet.
[].reduce($('.x'), function(i,v) {return i+v.outerHTML}, '')
or event more sweet with help of arrow functions
[].reduce.call($('.x'), (i,v) => i+v.outerHTML, '')
or without jQuery at all
[].reduce.call(document.querySelectorAll('.x'), (i,v) => i+v.outerHTML, '')
or if you don't like this approach, check that
$('.x').get().reduce((i,v) => i+v.outerHTML, '')
This is great for changing elements on the dom but does NOT work for ie when passing in a html string into jquery like this:
$('<div id="foo">Some <span id="blog">content</span></div>').find('#blog').outerHTML();
After some manipulation I have created a function which allows the above to work in ie for html strings:
$.fn.htmlStringOuterHTML = function() {
this.parent().find(this).wrap('<div/>');
return this.parent().html();
};
$.html = el => $("<div>"+el+"</div>").html().trim();
I came across this while looking for an answer to my issue which was that I was trying to remove a table row then add it back in at the bottom of the table (because I was dynamically creating data rows but wanted to show an 'Add New Record' type row at the bottom).
I had the same issue, in that it was returning the innerHtml so was missing the TR tags, which held the ID of that row and meant it was impossible to repeat the procedure.
The answer I found was that the jquery remove() function actually returns the element, that it removes, as an object. So, to remove and re-add a row it was as simple as this...
var a = $("#trRowToRemove").remove();
$('#tblMyTable').append(a);
If you're not removing the object but want to copy it somewhere else, use the clone() function instead.
jQuery plugin as a shorthand to directly get the whole element HTML:
jQuery.fn.outerHTML = function () {
return jQuery('<div />').append(this.eq(0).clone()).html();
};
And use it like this: $(".element").outerHTML();
Pure JavaScript:
var outerHTML = function(node) {
var div = document.createElement("div");
div.appendChild(node.cloneNode(true));
return div.innerHTML;
};
$("#myNode").parent(x).html();
Where 'x' is the node number, beginning with 0 as the first one, should get the right node you want, if you're trying to get a specific one. If you have child nodes, you should really be putting an ID on the one you want, though, to just zero in on that one. Using that methodology and no 'x' worked fine for me.
Simple solution.
var myself = $('#div').children().parent();
$("#myTable").parent().html();
Perhaps I'm not understanding your question properly, but this will get the selected element's parent element's html.
Is that what you're after?

Javascript: Recursion, jQuery Error

Working on a navigation menu script with jQuery. The script is being designed with recursion so that there is no hard coded limit to the number of levels the menu has.
I'll start with the code:
navigationMenu.prototype.reset = function ( ulElement, colorIndex, colors ) { //Color index should always be 1 when directly calling this function
var listItems = $(ulElement.children);
var numItems = listItems.length;
var targetWidth = (100 / numItems) + '%';
listItems.each( function ( x ) {
var children = $(listItems[x].children);
var xT = $(listItems[x]).prop('tagName');
var subMenu = null;
children.each( function ( y ) {
var yT = $(children[y]).prop('tagName');
if (yT == 'UL') {
subMenu = $(children[y]);
} else if (yT == 'A') {
$(children[y]).css('background-color', colors[colorIndex-1]); //Offset by 1 to facilitate for 0 indexed arrays
$(children[y]).hover( function () { //Set hover color to the opposite
$(children[y]).css('background-color',colors[(3-colorIndex)-1]); //3-1 = 2 and 3-2 = 1, subtract 1 to facilitate 0 indexed arrays
}, function() {
$(children[y]).css('background-color',colors[colorIndex-1]); //3-1 = 2 and 3-2 = 1, subtract 1 to facilitate 0 indexed arrays
}); //Rest of style SHOULD be handled by css (width 100%, text color, text align)
}
});
if (subMenu !== null) { //Recurse
navigationMenu.prototype.reset(subMenu, (3 - colorIndex), colors); //Not defined?
}
if (xT == 'LI') { //Format the element
$(listItems[x]).css('width',targetWidth);
$(listItems[x]).css('background-color', colors[colorIndex]);
}
});
};
Next, The error:
Uncaught TypeError: Cannot read property 'firstChild' of null <-whitespace-> jquery-1.11.1.min.js:2
What concerns me is that the error does not seem to come directly from my code, rather, a function within the jQuery library; however, I'm placing good money on the fact that it is because of something I did wrong.
A live demo can be found here:
http://proofoftheilluminati.com/test/test.html
For an idea of the final look of the menu you can see the top level with hover effect and a simple JS script that maths the link widths here:
http://proofoftheilluminati.com/test/index.html
Script:
http://proofoftheilluminati.com/test/scripts/menu.js
I'm hosting a freshly downloaded copy of jQuery version 1.11.1:
http://proofoftheilluminati.com/test/scripts/jquery-1.11.1.min.js
What it should be doing:
Top level list should be orange with black over effect
second level list should be black with orange hover effect
third level list should be same as first, etc.
Positioning is handled by external css file
What it is doing:
Handles top level list correctly, seems to error before style second level list.
Please let me know if I left anything out. I try to be thorough.
Edit: The supplied code has a comment on the line that calls itself:
//Not defined?
This was left over from a previous error, I was having trouble getting it to recognize the recursive function call. I tried the following lines here and they would not allow the function to progress:
this.reset(subMenu, (3 - colorIndex), colors);
reset(subMenu, (3 - colorIndex), colors);
navigationMenu.reset(subMenu, (3 - colorIndex), colors);
Additionally, this function is called when the document is ready:
$(document).ready(function() {
s = new navigationMenu('#NavMenu', '#884106', '#000000', -1);
});
Edit: modified code to use x/y instead of index and xT/yT instead of tag (removed nested variables with same name)
When you first call navigationMenu.prototype.reset, I'm guessing ulElement is a DOM element, but when you call it recursively, you are passing it subMenu, which is a jQuery object. That will be a problem for the following line:
var listItems = $(ulElement.children);
Try changing the following line of code:
navigationMenu.prototype.reset(subMenu, (3 - colorIndex), colors);
To:
navigationMenu.prototype.reset(subMenu[0], (3 - colorIndex), colors);
I prefer to always prefix variables that refer to jQuery objects with "$" to keep them straight.
You could also use this inside the functions given to .each(). So instead of:
children.each(function(index) {
var tag = $(children[index]).prop('tagName');
You could have:
children.each(function() {
var $child = $(this),
tag = $child.prop('tagName');
You could also consider using the jQuery .children() method, instead of the children DOM element property

How do you find the (string) length of a starting tag or ending tag?

I'm trying to write a jQuery or pure Javascript function (preferring the more readable solution) that can count the length of a starting tag or ending tag in an HTML document.
For example,
<p>Hello.</p>
would return 3 and 4 for the starting and ending tag lengths. Adding attributes,
<span class="red">Warning!</span>
would return 18 and 7 for the starting and ending tag lengths. Finally,
<img src="foobar.png"/>
would return 23 and 0 (or -1) for the starting and ending tag lengths.
I'm looking for a canonical, guaranteed-to-work-according-to-spec solution, so I'm trying to use DOM methods rather than manual text manipulations. For example, I would like the solution to work even for weird cases like
<p>spaces infiltrating the ending tag</ p >
and
<img alt="unended singleton tags" src="foobar.png">
and such. That is, my hope is that as long as we use proper DOM methods, we should be able to find the number of characters between < and > no matter how weird things get, even
<div data-tag="<div>">HTML-like strings within attributes</div>
I have looked at the jQuery API (especially the Manipulation section, including DOM Insertion and General Attributes subsections), but I don't see anything that would help.
Currently the best idea I have, given an element node is
lengthOfEndTag = node.tagName.length + 3;
lengthOfStartTag = node.outerHTML.length
- node.innerHTML.length
- lengthOfEndTag;
but of course I don't want to make such an assumption for the end tag.
(Finally, I'm familiar with regular expressions—but trying to avoid them if at all possible.)
EDIT
#Pointy and #squint helped me understand that it's not possible to see </ p >, for example, because the HTML is discarded once the DOM is created. That's fine. The objective, adjusted, is to find the length of the start and end tags as would be rendered in outerHTML.
An alternate way to do this could be to use XMLSerializer's serializeToString on a clone copy of the node (with id set) to avoid having to parse innerHTML, then split over "><"
var tags = (function () {
var x = new XMLSerializer(); // scope this so it doesn't need to be remade
return function tags(elm) {
var s, a, id, n, o = {open: null, close: null}; // spell stuff with var
if (elm.nodeType !== 1) throw new TypeError('Expected HTMLElement');
n = elm.cloneNode(); // clone to get rid of innerHTML
id = elm.getAttribute('id'); // re-apply id for clone
if (id !== null) n.setAttribute('id', id); // if it was set
s = x.serializeToString(n); // serialise
a = s.split('><');
if (a.length > 1) { // has close tag
o.close = '<' + a.pop();
o.open = a.join('><') + '>'; // join "just in case"
}
else o.open = a[0]; // no close tag
return o;
}
}()); // self invoke to init
After running this, you can access .length of open and close properties
tags(document.body); // {open: "<body class="question-page">", close: "</body>"}
What if an attribute's value has >< in it? XMLSerializer escapes this to >< so it won't change the .split.
What about no close tag? close will be null.
This answer helped me understand what #Pointy and #squint were trying to say.
The following solution works for me:
$.fn.lengthOfStartTag = function () {
var node = this[0];
if (!node || node.nodeType != 1) {
$.error("Called $.fn.lengthOfStartTag on non-element node.");
}
if (!$(node).is(":empty")) {
return node.outerHTML.indexOf(node.innerHTML);
}
return node.outerHTML.length;
}
$.fn.lengthOfEndTag = function () {
var node = this[0];
if (!node || node.nodeType != 1) {
$.error("Called $.fn.lengthOfEndTag on non-element node.");
}
if (!$(node).is(":empty")) {
var indexOfInnerHTML = node.outerHTML.indexOf(node.innerHTML);
return node.outerHTML.length - (indexOfInnerHTML + node.innerHTML.length);
}
return -1;
}
Sample jsFiddle here.

Using JavaScript & jQuery in a single function (Nodes & Stuff)

I am currently learning jQuery. I know that jQuery is a custom library for JavaScript.
I am doing some learning examples in a book that is only using JavaScript, and to further my learning experience, I am trying to make use of jQuery for anything that might be more efficient.
So, I have this code:
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
if (document.getElementById('currentWord').childNodes.length > 0) {
$('#currentWord p').append(letter);
} else {
var p = document.createElement('p');
var txt = document.createTextNode(letter);
p.appendChild(txt);
$('#currentWord').append(p);
}
}
Question #1:
If I change document.getElementById('currentWord').childNodes.length to $('#currentWord').childNodes.length it doesn't work. I thought the jQuery selector was the same thing as the JS document.getElementById as that it brought me back the DOM element. If that was the case, it'd make sense to be able to use the .childNodes.length functions on it; but it doesn't work. I guess it's not the same thing?
Question #2:
The code is textbook code. I have added all the jQuery that there is in it. My jQuery knowlede is limited, is there a more efficient way to execute the function?
The function's purpose:
This function is supposed to create a p element and fill it with a Text Node if it's the first time it's run. If the p element has already been created, it simply appends characters into it.
This is a word generating game, so you click on a letter and it gets added to a 'currentWord' div. The tile's letter is embedded in the 3rd css class, hence the attr splitting.
Thanks!
document.getElementById('currentWord')
returns a DOM object whereas $('#currentWord') returns a DOM object wrapped inside a jQuery object.
To get the plain DOM object you can do
$('#currentWord').get(0)
So
$('#currentWord').get(0).childNodes.length
should work.
Question #1:
jQuery returns a jQuery object. To return it to a regular javascript object use $(object)[0] and you can then treat it as a plain javascript (or DOM) object.
Question #2:
The efficiency looks good to me. Although you might want to use spans instead of p elements.
I guess one thing you could do (even though yours looks to run very fast) is cache the dom element:
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
var currentWord = document.getElementById('currentWord');
if (currentWord.childNodes.length > 0) {
$(currentWord).find('p').append(letter);
} else {
var p = document.createElement('p');
p.innerHTML = letter;
currentWord.appendChild(p);
}
}
Calls to the jQuery() function ($()) return a jQuery object containing the matching elements, not the elements themselves.
Calling $('#some-id') will, then, return a jQuery object that contains the element that would be selected by doing document.getElementById('some-id'). In order to access that element directly, you can get it out of that jQuery object, using either the .get() function or an array index syntax: $('#some-id')[0] (it's 0-indexed).
I think you can replace all of this with a call to the text function.
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
var currentWordP = $('#currentWord p');
if (currentWordP.size() > 0) {
currentWordP.text(currentWordP.text() + letter);
} else {
$('#currentWord').append("<p>" + letter + "</p>");
}
}
1: Use $.get(0) or $[0] to get the DOM element. e.x. $('#currentWord')[0].childNodes.length.
2: Try this:
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
if ($('#currentWord p').length > 0) {
$('#currentWord p').append(letter);
} else {
$('#currentWord').append(
$('<p />', { text: letter })
);
}
}
Question #1:
document.getElementById returns DOM object. more
childNodes.length is property of Node object which is returned by document.getElementById.
jQuery selector returns jQuery object more. You can get DOM object from jQuery object using .get
$('#IDselector').get(0) = document.getElementById('IDselector')
Question #2:
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
if ($('currentWord p').length > 0) {
$('#currentWord p').append(letter);
} else {
var p = $('<p />').text(letter);
$('#currentWord').append(p);
}
}

Select Element By CSS style (all with given style)

Is there a way to select all elements that have a given style using JavaScript?
Eg, I want all absolutely positioned elements on a page.
I would assume it is easier to find elements by style where the style is explicitly declared:
the style is non-inherited (such as positioning)
the style is not the default (as would be position:static).
Am I limited to those rules? Is there a better method for when those rules apply?
I would happily to use a selector engine if this is provided by one (ideally Slick - Mootools 1.3)
EDIT:
I came up with a solution that will only work with above rules.
It works by cycling through every style rule, and then selector on page.
Could anyone tell me if this is better that cycling through all elements (as recommended in all solutions).
I am aware that in IE I must change the style to lowercase, but that I could parse all styles at once using cssText. Left that out for simplicity.
Looking for best practice.
var classes = '';
Array.each(documents.stylesheets, function(sheet){
Array.each(sheet.rules || sheet.cssRules, function(rule){
if (rule.style.position == 'fixed') classes += rule.selectorText + ',';
});
});
var styleEls = $$(classes).combine($$('[style*=fixed]'));
You can keep Mootools, or whatever you use... :)
function getStyle(el, prop) {
var view = document.defaultView;
if (view && view.getComputedStyle) {
return view.getComputedStyle(el, null)[prop];
}
return el.currentStyle[prop];
}
​function getElementByStyle(style, value, tag)​ {
var all = document.getElementsByTagName(tag || "*");
var len = all.length;
var result = [];
for ( var i = 0; i < len; i++ ) {
if ( getStyle(all[i], style) === value )
result.push(all[i]);
}
return result;
}
For Mootools:
var styleEls = $$('*').filter(function(item) {
return item.getStyle('position') == 'absolute';
});
In jQuery you could use
$('*').filter( function(){
return ($(this).css('position') == 'absolute');
} );
[update]
Or even create a new selector.
got me interested and so here is one (its my 1st, so its not built for efficiency) to find elements by css property..
$.expr[':'].css = function(obj, index, meta, stack){
var params = meta[3].split(',');
return ($(obj).css(params[0]) == params[1]);
};
usage: $('optionalSelector:css(property,value)')
will return all elements (of optionalSelector) whose property = value
example: var visibleDivs = $('div:css(visibility,visible)');
will return all divs whose visibility is set to visible (works for the default visibility as well..)
There is no selector for CSS attributes, so you're pretty much stuck to looping through each element and checking it's position. Here's a jQuery method:
$("*").each(function() {
var pos = $(this).css('position');
if(pos == "absolute") {
// do something
}
else if (pos == "relative") {
// do something else
}
});
You can use Case statements instead of if/else as well.
Other than this solution, there is no selector per se that can search by CSS attributes (unless they were inline, maybe).

Categories

Resources