jQuery - has class that starts with - javascript

What would be the best way to get all divs that have any class that starts with input? In other words, a and b should be returned from what's below, but not c.
<div id="a" class="input1 foo"></div>
<div id="b" class="foo input2"></div>
<div id="c" class="xinput3 foo"></div>
The ostensible way, which surprisingly was accepted here, is to do $("div[class^='input']"); but of course that misses b. And of course $("div[class*='input']"); will give a false positive on c.
The best I could come up with was this monstrosity
function getAllInputDivs() {
return $("div").filter(function (i, currentDiv) {
return $.grep($(currentDiv).attr("class").split(" "), function (val) {
return val.substr(0, "input".length) === "input";
}).length > 0;
});
}
Is there a cleaner way? Here's a working fiddle of the above

You can create your own expression in jQuery
$.expr[':'].hasClassStartingWithInput = function(obj){
return (/\binput/).test(obj.className);
};
and you can retrieve those div with
$('div:hasClassStartingWithInput');
a JsFiddle Example: http://jsfiddle.net/7zFD6/
Edit: you could also use a parameter (without hardcoding the class name inside the function identifier) in this way
$.expr[':'].hasClassStartingWith = function(el, i, selector) {
var re = new RegExp("\\b" + selector[3]);
return re.test(el.className);
}
new example on http://jsfiddle.net/pMepk/1/

Here's one way...
function getAllInputDivs() {
return $("div").filter(function () {
return /(?:^|\s)input/.test(this.className);
});
}
Or make it more versatile...
function classStartsWith( tag, s ) {
var re = new RegExp('(?:^|\\s)' + s);
return $(tag || '*').filter(function () {
return re.test(this.className);
});
}
Or take the indexOf approach if you don't like regex...
function classStartsWith( tag, s ) {
return $(tag || '*').filter(function () {
return this.className.indexOf(s)===0 || this.className.indexOf(' ' + s)>-1;
});
}
Though you should be aware that it does't test for tab characters, only space characters, so it could fail if a tab was used instead of a space.
Going back to the regex versions, you can improve efficiency by adding the searched string to the selector.
Then it is only testing a subset of divs.
function getAllInputDivs() {
return $("div[class*='input']").filter(function () {
return /(?:^|\s)input/.test(this.className);
});
}
With the .filter() applied to only those divs that you know have input somewhere in the class, the performance will improve.
Or the versatile version would look like this:
function classStartsWith( tag, s ) {
var re = new RegExp('(?:^|\\s)' + s);
return $((tag || '*') + '[class*="' + s + '"]').filter(function () {
return re.test(this.className);
});
}

This is my solution for the problem:
(function($) {
$.fn.hasClassStartsWith = function(klass) {
var _return = [];
for(var i = 0; i < this.length; i++){
if((' ' + $(this[i]).attr('class')).indexOf(klass) != -1)
_return.push(this[i]);
}
return _return;
}
})(jQuery);
Use it as follows:
var divs = $('div').hasClassStartsWith("my_class_prefix");
It works also for the case someone creates a class with a dot in the middle.

Related

Replace <pre> without <code> to <pre> <code> once only

How to replace just <pre> which is not already wrapping <code>
with <pre><code> using jquery or javascript.
Example:
I have code like:
<pre><p>Hello</p></pre> <-- Target
<pre><code><p>Hello World</p></code></pre>
I need change only <pre> like this:
<pre><code><p>Hello</p></code></pre> <-- Only change this <pre>
<pre><code><p>Hello World</p></code></pre>
Check my problem below site: http://www.webmanajemen.com/p/testting-page.html
Thank to "Alon Eitan".
I have done with my problem using this jquery:
$("pre:not(:has(code))").each(function() { $(this).wrapInner( "<code></code>") });
Check my problem below site: web-development.cf/p/testting-page.html
You question is definitely unclear, but if I understand correctly, you want to basically add a tag into any tags that don't already contain .
This can be done by getting all of the tags, and then looking at its children to see if there is a tag:
function fillInMissingTags(outerTag, innerTag) {
var elements = document.getElementsByTagName(outerTag);
for(var elementIndex = 0, elementsLength = elements.length; elementIndex < elementsLength; elementIndex++) {
var element = elements[elementIndex],
found = false
html;
for(var childIndex = 0, childrenLength = element.children.length; childIndex < childrenLength; childIndex++) {
var child = element.children[childIndex];
if(child.tagName.toLowerCase() === innerTag) {
found = true;
html = element.innerHTML;
break;
}
}
if(found !== true) {
var codeElement = document.createElement(innerTag);
codeElement.innerHTML = html;
element.innerHTML = codeElement;
}
}
}
fillInMissingTags('pre', 'code');
This would only handle checking for in the immediate children. If you wanted to do a deep check, you would need to modify it to check the children recursively.
EDIT
I made it more generic so you can use the method to wrap the insides of any element that is missing a proper child.
Ick.
Regex I guess.
function replace(html) {
return html
// Look-ahead for <code>
.replace(/(<pre>)(?!<code>)/gi, '<pre><code>')
// Look-behind for </code>
.replace(/(<\/pre>)/gi, function(m, g, i, s) {
return s.slice(i-7, i) === '</code>'
? m
: '</code></pre>'
})
}
replace('<pre><p>Hello</p></pre>')
// returns "<pre><code><p>Hello</p></code></pre>"
replace('<pre><code><p>Hello World</p></code></pre>')
// returns "<pre><code><p>Hello World</p></code></pre>"

Javascript typing effect

The issue arises from the same issue as last time. My websites run off a static domain, so I want to be able to use this script on each site without making duplicate copies.
It functions as a typing text effect, I want to be able to define the text it prints out from the webpage itself and not the script.
Javascript
var index = 0;
var text = 'Text';
function type()
{
document.getElementById('screen').innerHTML += text.charAt(index);
index += 1;
var t = setTimeout('type()',100);
}
I've tried fiddling with the code and using them same method as my previous post, but I can't seem to get it to work.
Okay, I don't like any of the above code. Your original code also doesn't stop running once it reaches the end of the input text, and I don't believe any of the other suggested solutions stop either.
Here's a rewritten function in pure JS:
function type(i, t, ie, oe) {
input = document.getElementById(ie).innerHTML;
document.getElementById(oe).innerHTML += input.charAt(i);
setTimeout(function(){
((i < input.length - 1) ? type(i+1, t, ie, oe) : false);
}, t);
}
Which you can call like so:
type(0, 100, "text", "screen");
The parameters are: beginning index, speed, input element, output element
Your HTML will look something like this:
<div id="screen"></div>
<div id="text" style="display:none">Hello Bobby</div>
You can rename the divs to whatever you like, as long as you update the parameters accordingly. I'm sure there's an easier way to write this as well, but I like this method the most.
Demo
function type(i, t, ie, oe) {
input = document.getElementById(ie).innerHTML;
document.getElementById(oe).innerHTML += input.charAt(i);
setTimeout(function(){
((i < input.length - 1) ? type(i+1, t, ie, oe) : false);
}, t);
}
type(0, 100, "text", "screen");
<div id="screen"></div>
<div id="text" style="display:none">Hello Bobby</div>
Nice question, LMGTFY has often given me a giggle in the past. I think you may find the following to be pretty easy to throw around anywhere. It's just a few attributes added to your target container, along with a call to get the typewriter started.
Here, I run 4 of them simultaneously just for kicks. It's probably worth junking forEachNode in this example, instead using the few commented lines. If the result of getElementsByClassName was a true array, you could just call the .forEach method that arrays have. Unfortunately, a nodeList is similar but not the same - hence the need for such a function. I used it before realizing it probably clearer to do without it. In any case, it's a function I've found handy many times. I'll leave that in there as a thanks for such a fun question to consider.
function forEachNode(nodeList, func) {
var i, n = nodeList.length;
for (i = 0; i < n; i++) {
func(nodeList[i], i, nodeList);
}
}
window.addEventListener('load', mInit, false);
function typeWriter(el) {
var myDelay = el.getAttribute('keyDelay');
if (el.getAttribute('curIndex') == undefined)
el.setAttribute('curIndex', 0);
var curIndex = el.getAttribute('curIndex');
var curStr = el.getAttribute('typewriterdata');
el.innerHTML += curStr.charAt(curIndex);
curIndex++;
el.setAttribute('curIndex', curIndex);
if (curIndex < curStr.length)
setTimeout(callback, myDelay);
else {
if (el.getAttribute('nextline') != undefined) {
var nextTgt = el.getAttribute('nextline');
typeWriter(document.getElementById(nextTgt));
}
}
function callback() {
typeWriter(el);
}
}
function mInit() {
typeWriter(document.getElementById('line1'));
var i, n, elementList;
elementList = document.getElementsByClassName('autoType');
forEachNode(elementList, typeWriter);
// n = elementList.length;
// for (i=0; i<n; i++)
// typeWriter( elementList[i] );
}
.multi {
border: solid 2px #333333;
width: 400px;
}
<body>
<div class='autoType' typewriterdata='Enter this string letter by letter' keydelay='300'></div>
<div class='autoType' typewriterdata='Enter this string letter by letter' keydelay='200'></div>
<div class='autoType' typewriterdata='This is short but slooooow' keydelay='1000'></div>
<div class='autoType' typewriterdata='The rain falls mainly on the plain in Spain' keydelay='100'></div>
<div class='multi'>
<div id='line1' typewriterdata='This is line 1' keydelay='300' nextline='line2'></div>
<div id='line2' typewriterdata='This is line 2' keydelay='300' nextline='line3'></div>
<div id='line3' typewriterdata='This is line 3' keydelay='300' nextline='line4'></div>
<div id='line4' typewriterdata='This is line 4' keydelay='300'></div>
</div>
</body>
You can embed the text in the webpage itself in a hidden element like this:
HTML
<span id="hiddenText" style="display: none">Text you want to type out.</span>
and then you can get the text from the webpage itself like this:
Javascript
var text = document.getElementById('hiddenText').innerHTML;
Here is the jsfiddle you can see: http://jsfiddle.net/FMq6d/ .
This makes minimal changes to your code.
2 Years Later:
Check out this awesome Typing & erasing effect plus a blinking cursor - CodePen
In a Nutshell:
var index = 0;
var text = "The Typing Effect - In a Nutshell";
function type(){
var screenEl = $('#screen');
screenEl.html(text.substr(0, index++));
if (index < text.length) {
// Feel free to type
setTimeout('type()', 50);
} else {
// Reset and restart.
index = 0;
text = '';
}
};
type();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="screen"></p>
If you want to define what text it prints out, you should pass the text through an argument, if I understand your question correctly.
Try and mess with this:
var type = function( elem , text , index )
{
var index = index||0;
elem.innerHTML += text.charAt(index);
index++;
var t = setTimeout(function(){
type( elem , text , index );
},100);
}
type( document.getElementById('screen') , 'How\'re You?' );
<p id="screen">Hello, </p>
Here is an approach using promises for sleeping between key presses.
Here is a link for the repo at Github, but the code is basically this:
class Typer {
constructor(typingSpeed, content, output) {
this.typingSpeed = typingSpeed;
// Parses a NodeList to a series of chained promises
this.parseHtml(Array.from(content), output);
};
makePromise(node, output) {
if (node.nodeType == 1) // element
{
// When a new html tag is detected, append it to the document
return new Promise((resolve) => {
var tag = $(node.outerHTML.replace(node.innerHTML, ""));
tag.appendTo(output);
resolve(tag);
});
} else if (node.nodeType == 3) // text
{
// When text is detected, create a promise that appends a character
// and sleeps for a while before adding the next one, and so on...
return this.type(node, output, 0);
} else {
console.warn("Unknown node type");
}
}
parseHtml(nodes, output) {
return nodes.reduce((previous, current) => previous
.then(() => this.makePromise(current, output)
.then((output) => this.parseHtml(Array.from(current.childNodes), output))), Promise.resolve());
}
type(node, output, textPosition) {
var textIncrement = textPosition + 1;
var substring = node.data.substring(textPosition, textIncrement);
if (substring !== "") {
return new Promise(resolve => setTimeout(resolve, this.typingSpeed))
.then(() => output.append(substring))
.then(() => this.type(node, output, textIncrement));
}
return Promise.resolve(output);
}
}
let typeSpeed = 300;
let deleteSpeed = 200;
let wordDelay = 1000;
// utility function that returns a promise that resolves after t milliseconds
const delay = (t) => {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
//Change Current Job
const changeCurrentJob = async (wordsJson) => {
//Get Current Job
let currentJob = document.getElementById('wrap');
for (let wordFromJson of wordsJson) {
//Deleting
//Previous word letters count
let prevLetters = currentJob.innerHTML.split('');
//Loop letters with for of to remove them
for(let letterFromWordPrev of currentJob.innerHTML){
//Remove Last letter
prevLetters.pop();
//Join Letters Array
currentJob.innerHTML = prevLetters.join('');
await delay(deleteSpeed);
}
//Typing
for(let letterFromWord of wordFromJson){
currentJob.innerHTML = currentJob.innerHTML+letterFromWord;
//Type Speed
await delay(typeSpeed);
}
//After finishing word Wait
await delay(wordDelay);
}
//ReDO Typing - Declare Variables then Redo -
let words = document.getElementsByClassName('typewrite');
let wordsData = words[0];
let wordsJson2 = JSON.parse(wordsData.getAttribute('data-type'));
changeCurrentJob(wordsJson2);
}
// On window load Loop data-type And convert from json to txt and type
window.onload = function() {
let words = document.getElementsByClassName('typewrite');
let wordsData = words[0];
let wordsJson = JSON.parse(wordsData.getAttribute('data-type'));
setTimeout(changeCurrentJob,wordDelay,wordsJson);
};
<div class="typewrite" data-type='[ "Full Stack", "PHP", "JS" ]'>
<div class="styledWarp" id="wrap"></div>
</div>

Find HTML based on partial attribute

Is there a way with javascript (particularly jQuery) to find an element based on a partial attribute name?
I am not looking for any of the selectors that find partial attribute values as referenced in these links:
starts with [name^="value"]
contains prefix [name|="value"]
contains [name*="value"]
contains word [name~="value"]
ends with [name$="value"]
equals [name="value"]
not equal [name!="value"]
starts with [name^="value"]
but more something along the lines of
<div data-api-src="some value"></div>
<div data-api-myattr="foobar"></div>
and
$("[^data-api]").doSomething()
to find any element that has an attribute that starts with "data-api".
This uses .filter() to limit the candidates to those that has data-api-* attributes. Probably not the most efficient approach, but usable if you can first narrow down the search with a relevant selector.
$("div").filter(function() {
var attrs = this.attributes;
for (var i = 0; i < attrs.length; i++) {
if (attrs[i].nodeName.indexOf('data-api-') === 0) return true;
};
return false;
}).css('color', 'red');
Demo: http://jsfiddle.net/r3yPZ/2/
This can also be written as a selector. Here's my novice attempt:
$.expr[':'].hasAttrWithPrefix = function(obj, idx, meta, stack) {
for (var i = 0; i < obj.attributes.length; i++) {
if (obj.attributes[i].nodeName.indexOf(meta[3]) === 0) return true;
};
return false;
};
Usage:
$('div:hasAttrWithPrefix(data-api-)').css('color', 'red');
Demo: http://jsfiddle.net/SuSpe/3/
This selector should work for pre-1.8 versions of jQuery. For 1.8 and beyond, some changes may be required. Here's an attempt at a 1.8-compliant version:
$.expr[':'].hasAttrWithPrefix = $.expr.createPseudo(function(prefix) {
return function(obj) {
for (var i = 0; i < obj.attributes.length; i++) {
if (obj.attributes[i].nodeName.indexOf(prefix) === 0) return true;
};
return false;
};
});
Demo: http://jsfiddle.net/SuSpe/2/
For a more generic solution, here's a selector that takes a regex pattern and selects elements with attributes that match that pattern:
$.expr[':'].hasAttr = $.expr.createPseudo(function(regex) {
var re = new RegExp(regex);
return function(obj) {
var attrs = obj.attributes
for (var i = 0; i < attrs.length; i++) {
if (re.test(attrs[i].nodeName)) return true;
};
return false;
};
});
For your example, something like this should work:
$('div:hasAttr(^data-api-.+$)').css('color', 'red');
Demo: http://jsfiddle.net/Jg5qH/1/
Not sure what it is you're looking for, but just spent a few minutes writing this:
$.fn.filterData = function(set) {
var elems=$([]);
this.each(function(i,e) {
$.each( $(e).data(), function(j,f) {
if (j.substring(0, set.length) == set) {
elems = elems.add($(e));
}
});
});
return elems;
}
To be used like :
$('div').filterData('api').css('color', 'red');
And will match any elements with a data attribute like data-api-*, and you can extend and modify it to include more options etc. but of right now it only searches for data attributes, and only matches 'starts with', but at least it's simple to use ?
FIDDLE

What is the best way to check if element has a class?

The problem
If the element has multiple classes then it will not match with the regular property value checking, so I'm looking for the best way to check if the object has a particular class in the element's className property.
Example
// element's classname is 'hello world helloworld'
var element = document.getElementById('element');
// this obviously fails
if(element.className == 'hello'){ ... }
// this is not good if the className is just 'helloworld' because it will match
if(element.className.indexOf('hello') != -1){ ... }
So what would be the best way to do this?
just pure javascript please
function hasClass( elem, klass ) {
return (" " + elem.className + " " ).indexOf( " "+klass+" " ) > -1;
}
In modern browsers, you can use classList:
if (element.classList.contains("hello")) {
// do something
}
In the browser that doesn't implement classList but exposes the DOM's prototype, you can use the shim showed in the link.
Otherwise, you can use the same shim's code to have a generic function without manipulate the prototype.
this 2018 use ES6
const hasClass = (el, className) => el.classList.contains(className);
How to use
hasClass(document.querySelector('div.active'), 'active'); // true
You ask for pure javascript, so this is how jQuery implement it:
hasClass: function( selector ) {
var className = " " + selector + " ",
i = 0,
l = this.length;
for ( ; i < l; i++ ) {
if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
return true;
}
}
return false;
},
rclass is a regular expression of [\n\t\r], to ensure that alternative methods of delimiting class names are not an issue. this would be jQuery's reference to the object(s) and in a sense it makes the code more complicated than required, but it should make sense without knowing the details of it.
This should work for you:
var a = [];
function getElementsByClassName(classname, node) {
if(!node) node = document.getElementsByTagName("body")[0];
var re = new RegExp('\\b' + classname + '\\b');
var els = node.getElementsByTagName("*");
for(var i=0, j=els.length; i<j; i++)
if(re.test(els[i].className)) a.push(els[i]);
return a;
}
getElementsByClassName('wrapper');
for (var i=0; i< a.length; i++) {
console.log(a[i].className);
}
This code will traverse the DOM and search for classes defined as parameter in the main function.Then it will test each founded class elements with a regexp pattern. If it founds will push to an array, and will output the results.
First, split the className by using the " " character, then check the index of the class you want to find.
function hasClass(element, clazz) {
return element.className.split(" ").indexOf(clazz) > -1;
}

Rangy range contained within a jquery selector

I'm working on a JavaScript wrapper around the Rangy JavaScript plugin. What I'm trying to do: given a jQuery selector and a range, detect if the range is contained within the selector. This is for a space where a user will read a document and be able to make comments about particular sections. So I have a div with id="viewer" that contains the document, and I have an area of buttons that do things after a user selects some text. Here is the (broken) function:
function selectedRangeInRegion(selector) {
var selectionArea = $(selector);
var range = rangy.getSelection().getRangeAt(0);
var inArea = (selectionArea.has(range.startContainer).length > 0);
if (inArea) {
return range;
} else {
return null;
}
}
It appears that selectionArea.has(range.startContainer) returns an array of size 0. I have tried wrapping like: $(range.startContainer). Any tips?
I developed a solution for this problem. This assumes you have a div selector and that your content does not have any divs:
function containsLegalRange(selector, range) {
var foundContainingNode = false;
var container = range.commonAncestorContainer
var nearestDiv = $(container).closest("div");
if (nearestDiv.attr("id") == selector) {
return true
}
else {
return false
}
}
That's not how has() works: the parameter you pass to it is either a selector string or a DOM element, whereas range.startContainer is a DOM node that may in practice be a text node or an element.
I don't think there will be a way that's as easy as you're hoping. The following is as simple as I can think of off the top of my head.
jsFiddle: http://jsfiddle.net/TRVCm/
Code:
function containsRange(selector, range, allowPartiallySelected) {
var foundContainingNode = false;
$(selector).each(function() {
if (range.containsNode(this, allowPartiallySelected)) {
foundContainingNode = true;
return false;
}
});
return foundContainingNode;
}
.has() can be weird sometimes and produce .length == 0 when it is not supposed to. Try this way instead:
function selectedRangeInRegion(selector) {
var range = rangy.getSelection().getRangeAt(0);
var selectionArea = selector + ':has(\'' + range.startContainer + '\')';
var inArea = $(selectionArea).length > 0);
if (inArea) {
return range;
}
else {
return null;
}
}

Categories