I have the following line of code:
document.getElementById("question_*").setAttribute("disabled", "false");
I want to use a form of wildcard for the element ID. The script is run and used by lots of different buttons, they all have ID's of question_something - is it possible to make this setAttribute to make the button enabled for various ID's?
<button id="question_1a" onClick="nextQuestion(this.id)" disabled>Next question</button>
EDIT:
I've switched to a classname as suggested. Buttons now are:
<button id="question_1a" class="nextButton" disabled>Next question</button>
I've added the following line to make this not disabled:
var elems = document.getElementsByClassName('nextButton').setAttribute("disabled", "false");
But I get: Uncaught TypeError: Object # has no method 'setAttribute'
You can't use wildcards with document.getElementById(), you can, however, with document.querySelectorAll():
var elems = document.querySelectorAll('button[id^="question_"]');
This, of course, requires a relatively up to date browser; on the other hand using a class-name (for example question) to associate those elements would allow you to use:
var elems = document.getElementsByClassName('question');
Or:
var elems = document.querySelectorAll('button.question');
I tried doing this: var elems = document.getElementsByClassName('nextButton').setAttribute("disabled", "false"); - but I get: Uncaught TypeError: Object # has no method 'setAttribute'
That's because you can't modify the properties of a NodeList all at once, you can, however, use a for (){...} loop, or similar to do so:
Using for(){...}:
var elems = document.getElementsByClassName('question');
for (var i = 0, len = elems.length; i < len; i++){
elems[i].disabled = false; // to make them all enabled
}
JS Fiddle demo.
Or using forEach (up to date browsers):
var elems = document.getElementsByClassName('question');
[].forEach.call(elems, function(a){
a.disabled = false; // will make all elements enabled
});
JS Fiddle demo.
References:
Array.prototype.forEach().
CSS attribute-selectors.
document.getElementById().
document.getElementsByClassName().
document.querySelector() compatibility.
document.querySelectorAll() compatibility.
Function.prototype.call().
JavaScript for loop.
That's precisely what classes are for:
<button class="question" id="question_1a" onClick="nextQuestion(this.id)" disabled>Next question</button>
And
var elems = document.getElementsByClassName("question");
for(var z=0;z<elems.length; z++){
elems[z].setAttribute("disabled", "false")
}
Another possibility, if querySelectorAll or getElementsByClassName (could be shimmed in a similar manner) wasn't available and you wanted to match multiple IDs.
HTML
<button>Question</button>
<button id="question_1a">Question</button>
<button id="question_1b">Question</button>
<button id="question_1c">Question</button>
<button id="question_1d">Question</button>
<button id="question_2a">Question</button>
Javascript
function getElementsById(regex) {
var tags = document.getElementsByTagName('*'),
tagsLength = tags.length,
matches = [],
index,
tag;
for (index = 0; index < tagsLength; index += 1) {
tag = tags[index];
if (regex.test(tag.id)) {
matches.push(tag);
}
}
return matches;
}
console.log(getElementsById(/^question_1[a-z]?$/));
Output
[button#question_1a, button#question_1b, button#question_1c, button#question_1d]
On jsFiddle
Then you can iterate this array and set the attributes
And the getElementsByclassName shim
function getElementsByClassName(node, className) {
var array = [],
regex = new RegExp("(^| )" + className + "( |$)"),
elements = node.getElementsByTagName("*"),
length = elements.length,
i = 0,
element;
while (i < length) {
element = elements[i];
if (regex.test(element.className)) {
array.push(element);
}
i += 1;
}
return array;
}
Nope wildcard isn't supported. But to solve this problem you can use jQuery to do the same.
P. S. I will try to post code once I am back at desk
Update
agree David Thomas & Beemo's code hint that should solve your problem
You can use JQuery $("[id^='question_']") selector to get all elements with an id starting by question_
Related
I want to take the second class of an element, and then add a class to the element that has that ID. In Chrome, Firefox, etc. it works perfectly but in IE11 it does not. Do you know why? Help me please
for (var i = 0; i < $('#example .item').length; i++) {
var class_svg = document.getElementsByClassName("item")[i].className.split(' ')[1];
var $elem = document.getElementById(class_svg);
$elem.classList.add("show");
}
According to the docs classList is only partially supported before Edge. Use className instead like:
$elem.className += ' show';
edit: thanks for the hint #Sterling Archer
Creating a basic example using an SVG element (which I assume you have based on your example code) and looking at it in IE11 and Edge, you can see that the className property is a [object SVGAnimatedString]. That is some special object for SVG elements, instead of dealing with that special object, lets just deal with the attribute itself using getAttribute. Here is the code that works in most all browsers:
for (var i = 0; i < $('#example .item').length; i++) {
var class_svg = document.getElementsByClassName("item")[i].getAttribute("class").split(' ')[1];
var $elem = document.getElementById(class_svg);
var classList = $elem.getAttribute("class");
classList += " show";
$elem.setAttribute("class",classList);
}
Hope that works for you.
I want to get all elements from document which starts with ge-. Example <ge-survey></ge-survey> <ge-sombody></ge-sombody> <ge-...></ge-...>.
var elements = document.getElementsByTagName("*"); // <<-- now return all
also I tried :
var elements = document.querySelectorAll("^=ge-");
but get :
geadele.js:3 Uncaught SyntaxError: Failed to execute 'querySelectorAll' on 'Document': '^=ge-' is not a valid selector.
Also I tried:
var els = document.getElementsByTagName("^=ge-"); // return epty HTMLcollection
my html:
...
</head>
<body>
<ge-survey></ge-survey>
<ge-element></ge-element>
</body>
...
If you know all ge-elements, which you want to get, you can use document.getElementsByTagName('ge-somebody') and concatenate result.
var ge1Elements = Array.prototype.slice.call(document.getElementsByTagName('ge-1'));
var ge2Elements = Array.prototype.slice.call(document.getElementsByTagName('ge-2'));
...
var geElements = [].concat(ge1Elements, ge2Elements, ge3Elements);
In my opinion this approach in documents with a large number of elements is faster than regexp, but I didn't any benchmarks.
If you don't know all posible ge- elements, you can get it by filtering all elements:
var elements = document.getElementsByTagName("*");
var geElements = [];
for(var i = 0; i < elements.length; i++){
if(elements[i].tagName.indexOf('GE-') === 0){
geElements.push(elements[i]);
}
}
console.log(geElements);
You can use document.querySelector and use a 'starts with' type css selector:
Can I use a regular expression in querySelectorAll?
I'm using an array to append to a base class name so I can iterate through different classes.
First bit works fine. Get a count on the array and as long as I exclude the last two lines of code I get all the desired class names in the console.
But once I add the last two lines the loop and try to add the constructed class name to get the different text values per class I get the following error:
Uncaught Error: Syntax error, unrecognized expression: '.moduleStatusDIS'
And it stops at the first iteration through the array. Code below.
function setModuleStatusColour() {
var array = ["DIS", "DDG", "CDX", "DKM", "DBV", "DBB", "DGK", "DAM", "LOG", "DUS", "DCL", "DRI"];
var arrayLength = array.length;
console.log(arrayLength);
for (x=0; x < arrayLength; x++){
var className = "'"+'.moduleStatus'+array[x]+"'";
console.log(className);
var statusValue = $(className).text();
console.log(statusValue);
}
}
I also tried to use eval() (although I know it's been deprecated, desperation got the better of me) see below. Same result as code above though.
function setModuleStatusColour() {
var array = ["DIS", "DDG", "CDX", "DKM", "DBV", "DBB", "DGK", "DAM", "LOG", "DUS", "DCL", "DRI"];
var arrayLength = array.length;
console.log(arrayLength);
for (x=0; x < arrayLength; x++){
var className = "'"+'.moduleStatus'+array[x]+"'";
console.log(className);
eval('var statusValue = $(className).text()');
console.log(statusValue);
}
}
Even tried to cast the var className into a String but didn work for me either.
If I write it out class by class and don't use an array to construct the names it works fine but I tried to keep the code short and make it easy to add. So it has become a matter of principle :)
Any help would be greatly appreciated!
This line:
var className = "'"+'.moduleStatus'+array[x]+"'";
should be simply
var className = ".moduleStatus" + array[x];
giving you a selector like
var statusValue = $(".moduleStatusDIS").text();
Selectors are just strings, there is no need to wrap it in single quotes.
It should be easy, just:
function setModuleStatusColour() {
var array = ["DIS", "DDG", "CDX", "DKM", "DBV", "DBB", "DGK", "DAM", "LOG", "DUS", "DCL", "DRI"];
var arrayLength = array.length;
console.log(arrayLength);
for (x=0; x < arrayLength; x++){
var statusValue = $('.moduleStatus'+array[x]).text();
console.log(statusValue);
}
Selectors are conformed by: $(-string-) where -string- should be the name of a class preceded by a dot (".className"), or the name of an element like $("div") (but this will select all div elements!), or the id of an element preceded by a # symbol like $("#sendButton")... The reason why .className and #sendButton are surrounded by quotation is that they are strings that refer to attributes given to elements like so:
<div class="className">...</div> --> $(".className")
<div id="sendButton">..<div> --> $("#sendButton")
<div>...</div> --> $("div")
I would like to get all the elements/nodes in an HTML page which contain attributes that start with something (again, the attribute names start with something, not their values!). For example, TinyMCE has a tendency of adding custom attributes to the elements it saves, like "mce_style", "mce_href", "mce_bogus", etc. I would like to have something like the CSS3 selector for attribute values, [attr^="mce_"], but not for the values, the attribute names.
Of course, I can iterate through all DOM nodes and their attributes and check them one by one, but I was wondering whether there is a more efficient way.
Please don't give me TinyMCE-specific answers, I'm pretty sure there's a flag which would prevent TinyMCE for saving these attributes, but the question is generic.
here's a simple demo to find all elements that contain an attribute starting with mce_. might need some refinements.
function getMCE() {
var el, attr, i, j, arr = [],
reg = new RegExp('^mce_', 'i'), //case insensitive mce_ pattern
els = document.body.getElementsByTagName('*'); //get all tags in body
for (i = 0; i < els.length; i++) { //loop through all tags
el = els[i] //our current element
attr = el.attributes; //its attributes
dance: for (j = 0; j < attr.length; j++) { //loop through all attributes
if (reg.test(attr[j].name)) { //if an attribute starts with mce_
arr.push(el); //push to collection
break dance; //break this loop
}
}
}
return arr;
}
console.log(getMCE())
Try this:
FUNCTIONS
//custom selector expression
$.extend($.expr[':'],{
attr:function(o,i,m){
var attrs=$.getAttrAll(o),re=m[3],found=false;
$.each(attrs,function(k,v){
if(new RegExp(re).test(v)) { return found=true;}
});
return found;
}
});
// get all atrributes of an element
$.getAttrAll=function(el){
var rect = [];
for (var i=0, attrs=el.attributes, len=attrs.length; i<len; i++){
rect.push(attrs.item(i).nodeName);
}
return rect;
};
`
USAGE
// calling custom selector expression :attr(regexp)
$(function(){
$('body').find(':attr("^mce_")').css({background:'yellow'});
});
HTML
<body>
<p mce_style="height:50px" id="x" data-hello="hello">selected</p>
<div not_mce_bogus="abc">not_mce_bogus</div>
<div mce_href="http://rahenrangan.com">selected</div>
<p>othrs</p>
</body>
One option, if you don't mind temporarily altering your DOM, is to extract your HTML into a string and search for the attributes via RegExp. When you find the attributes, you could append a "needle" in the DOM so that you can use jQuery to select the elements.
Here is a working concept (run with console open):
http://jsfiddle.net/skylar/N43Bm/
Code:
$.fn.extend({
findAttributes: function(attribute) {
var attributeFinder = new RegExp(attribute + '(.+)="', "gi");
var elementHTML = this.html().replace(attributeFinder, "data-needle='pin' "+attribute+"$1=\"");
this.html(elementHTML);
return this.find("[data-needle=pin]").removeAttr('data-needle');
}
});
console.log($("body").findAttributes('mce_'));
Note: my regexp is not great. You'll have to take better care than I have in this example.
Try this: (I tried putting * instead of a tag but it colored all the elements including those who do not have mce_style attribute as well)
a[mce_style] { color : red; }
Demo : http://jsfiddle.net/Tcdmb/
More info : https://developer.mozilla.org/en/CSS/Attribute_selectors
I am trying to first find a div using a regular expression (since its class name is somewhat dynamic).
Once found, I then need to place the div inside of a fieldset, so I end up having a final output of
<fieldset class="...">
<div class="the one I found">...</div>
</fieldset>
How can I do this in javascript?
Much thanks,
Steve
This is going to be difficult to do with regexes and ill-advised. For example, what if the div contains other divs? Finding the correct closing div tag is not something a regular expression can do because HTML is not a regular language.
On the other hand, this is a trivial one liner with jQuery:
$("div.someClass").wrap("<fieldset class='...'></fieldset>");
It can of course be done with vanilla Javascript DOM using something like:
var divs = document.getElementsByTagName("div");
for (var i=0; i<divs.length; i++) {
if (divs[i].className == "...") {
var fs = document.createElement("fieldset");
fs.className = "...";
var parent = divs[i].parentNode;
parent.insertBefore(fs, divs[i]);
fs.appendChild(divs[i]);
}
}
You of course need to fill in what class to put on the fieldset and change the test on the div to figure out if you need to manipulate it or not.
using jquery, you can try this:
var classes = $(document.body).html().match(/class="pattern"/g); // find classname matchin pattern
for(i in classes) {
var nodes = $('.'+classes[i].substr (7, str.length - 8));
nodes.wrap("<fieldset class='...' />");
}
window.onload = function() {
var params = {
has: "something"
};
// var fieldset = doc... get field the same as with div.
var divs = document.getElementsByTagName("div");
for (var i = 0; i < divs.length; i++) {
if (params.has.indexOf(divs[i].className) > 0) {
// fieldset.innerHTML = divs[i].innerHTML;
divs[i].innerHTML = "<fieldset class=''> + divs[i].innerHTML + "</fieldset>";
}
}
}
No need to use regular expression, indexof method is sufficient. And no need to use jquery. Javascript has good string and array functions - use them, but the DOM is a mess.