I'm creating a form where you can choose between different sizes of paper, everything working well and I've made a search form in jQuery that looks in the text() of a div to find matching formats. In example: there's a size 105 x 148 and if I type "105" in #searchformat it hide()s all other divs without "105".
$('#searchformat').keyup(function() {
$('.awspc-image-selection-box').hide();
$('.awspc-image-selection-box-content .awspc-image-selection-box-content-label').each(function() {
if($(this).text().toLowerCase().replace(/\s/g, '').indexOf(""+text+"") != -1 ) {
$(this).closest('.awspc-image-selection-box').show();
}
});
});
This works great. There's just one thing I can't seem to get working, I want to have the ability if you type in the input field for example "104" that it will also show the divs with "105" in it. With a margin of +- 5.
The field where I need to search in is $('.awspc-image-selection-box-content-label').text() and I already tried it with a function having return Math.round(x.match(/\d+/) / text) * text; but this looks for the nearest number and the problem here also is that the value of the field is like this: <div class="awspc-image-selection-box-content-label"> 38.1 x 8 mm </div>
HTML:
<div class="awspc-image-selection-box" data-value="7" data-id="aws_price_calc_45" data-substrate="">
<div class="awspc-image-selection-box-content">
<img style="width: ; height: " src="/uploads/woocommerce-placeholder-768x768.png" alt="170">
<div class="awspc-image-selection-box-content-label">38.1 x 8 mm</div>
</div>
</div>
So how can I in example search for "39" and it will show me the div having "38"?
Thanks a lot!
UPDATE
I figured some working code out but the only problem now is how I can retrieve what the closest() div is from the result...
var tooSearchFor = $('.awspc-image-selection-box-content-label').text();
tooSearchFor = tooSearchFor.split('\n');
for(var i = 0; i < tooSearchFor.length; i++)
tooSearchFor[i] = parseInt(tooSearchFor[i]) || 0;
var goalFormat = textInputSearch;
var closestFormat = null;
$.each(tooSearchFor, function(){
if (closestFormat == null || Math.abs(this - goalFormat) < Math.abs(closestFormat - goalFormat)) {
closestFormat = this;
}
});
console.log ('Closest format:' + closestFormat); // outputs the closest found number
How can I retrieve the div where closestFormat is found in the array...?
Related
I am using jquery filter to search for names in a big list of names. It works just fine with good speed when I type in the input field. But when I press backspace button to clear the search text there is a delay of more then 4s when there are about 2 or 3 characters left.
I have made a demo to explain my problem
<input type="text" class="search">
<div class="list"></div>
<script>
// Get the list div
let $list = $('.list');
// Form a list of numbers from 1 to 8000
let listHtml = ''
for (let i = 0; i < 8000; i++) {
listHtml += `<div class="list_item"><div class="list_item_value c${i}">${i}</div></div>`;
}
$list.html(listHtml);
// Get all the list items
$listItem = $list.find('.list_item');
$('.search').on('keyup', function(e) {
// Get the search text
let text = $(this).val();
$listItem.filter(function() {
$(this).toggle($(this).find(`.list_item_value`).text().includes(text));
});
});
</script>
I have simplified my problem with this demo by replacing the text search by number search.
One way to fix this problem would be by canceling the ongoing jquery filter process when a backspace is pressed. But I don't know how to do that. Please someone help me fix it.
Consider the following example.
$(function() {
function makeItems(n, t) {
for (var i = 1; i < n; i++) {
$(t).append("<div class='list_item'><div class='list_item_value " + i + "'>" + i + "</div>");
}
}
var $list = $('.list');
makeItems(8000, $list);
$('.search').keyup(function(e) {
var text = $(this).val().toLowerCase();
$(".filtered").removeClass("filtered");
var search = (text.length > 0);
if (search) {
$(".list_item", $list).filter(function(index) {
return $(this).text().indexOf(text) === -1;
}).addClass("filtered");
}
});
});
.filtered {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" class="search">
<div class="list"></div>
Not too much different from your code. Some reduction of code. When the Search is done, there is a condition to ensure it's not done on an empty value. Also we reset the view each time. Show, Hide, and Toggle have easing and callbacks, so they can be slower at times. Add/Remove class is very quick.
https://ryanpeden.com/javascript-indexof-vs-includes-performance/
So while includes will be a tiny, tiny amount slower because it has to check if you passed it a regex, in reality this will make no difference to how fast your code runs. You should use indexOf if you care about where the substring is in the original string. If you don’t care, just call includes because it makes the intent of your code more clear.
In the end, it's your call if you want to use includes or indexOf for your comparison.
I wrote a JavaScript function that takes the current number of spans of the class mini in the paragraph element with an id mega, which is at least 1, and if there are less than 4, adds enough to make 4. If there was no second mini, then the second mini, which the function should create, should say 2nd, if a third one is created, it should say 3rd, and if a fourth is created, it should say 4th. For example, if there are already 2 mini spans, the program, should add 2 more, the first one added saying 3rd and the second one saying 4rd. Here's the code:
function addSpans(currentNumOfSpans)
{
var mega = document.getElementById("mega");
var mini = document.createElement("span");
mini.className = "mini";
if (currentNumOfSpans < 4)
{
if (currentNumOfSpans < 3)
{
if (currentNumOfSpans < 2)
{
mini.innerHTML = "2<sup>nd</sup>;
mega.appendChild(mini);
}
mini.innerHTML = "3<sup>rd</sup>;
mega.appendChild(mini);
}
mini.innerHTML = "4<sup>th</sup>;
mega.appendChild(mini);
}
}
Soooo.... If currentNumOfSpans is 3, it works fine, and adds 4th to mega. However, if currentNumOfSpans is 1 or 2, while it should add 2nd3rd4th or 3rd4th, respectively, it just adds 4th. Can someone help me figure out what's wrong with this. Any help's appreciated, thanks!
Note: If you notice any typos, please comment or edit, but they aren't the problem, I've checked over my code in a syntax checker, but I often make errors in my code on SO because I use a tiny phone keyboard. So basically, typo's, whichI probably made, aren't the real problem. Thanks!
Your example included a few typos, most of which could be found by running your code through a debugger, like http://jshint.com.
However, I would use a more functional approach. The following method is not hard coded like yours, so you could use it for multiple elements, or use a different number of spans with very minimal changes to the usage, I've shown this in the demo below.
function getSuffix(i) {
var j = i % 10, k = i % 100;
if (j == 1 && k != 11) return i + "<sup>st</sup>";
if (j == 2 && k != 12) return i + "<sup>nd</sup>";
if (j == 3 && k != 13) return i + "<sup>rd</sup>";
return i + "<sup>th</sup>";
}
function addSpans(scope, length) {
var spans = scope.querySelectorAll('.mini');
var current = length - (length - spans.length);
while(current < length) {
var span = document.createElement('span');
span.className = 'mini';
span.innerHTML = getSuffix(++current);
scope.appendChild(span);
}
}
var wrap = document.querySelector('.wrap'), divs;
var clone = wrap.cloneNode(true);
wrap.parentNode.appendChild(clone);
divs = wrap.querySelectorAll('.mega');
for(var i in Object.keys(divs)) addSpans(divs[i], 4);
divs = clone.querySelectorAll('.mega');
for(var i in Object.keys(divs)) addSpans(divs[i], 6 + (i * 2));
.mega { font-size: 0; } .mini { display: inline-block; width: 40px; font-size: 16px; }
<div class="wrap">
<div class="mega"></div>
<div class="mega"><span class="mini">1<sup>st</sup></span></div>
<div class="mega"><span class="mini">1<sup>st</sup></span><span class="mini">2<sup>nd</sup></span></div>
<div class="mega"><span class="mini">1<sup>st</sup></span><span class="mini">2<sup>nd</sup></span><span class="mini">3<sup>rd</sup></span></div>
<div class="mega"><span class="mini">1<sup>st</sup></span><span class="mini">2<sup>nd</sup></span><span class="mini">3<sup>rd</sup></span><span class="mini">4<sup>th</sup></span></div>
</div>
I apologise in advance, I'm not allowed to post images until I have 10 reputation. So, I hope that my description would be enough to get across the idea I have.
So, say I have three columns; a, b, and c. And when there's too much content to be hosted in just a, b, and c, I'd want new off-screen columns to be made, d, e, and f. - This goes on until all the content is used.
So, my current setup has the "hidden-text" div play host to all the content, and then I'd have a JavaScript function, or jQuery function to dynamically populate each of the divs, and create divs where needed along with buttons to then get to the new divs it creates.
The way it determines which to populate is simply grabbing the content and putting it into column a. Column a is full when the content reaches the bottom of the screen. Then it grabs the rest of the content and puts it in b, until b is full and so on until all the content available is used.
I really hope I'm being clear enough, I have no idea if anyone is going to even remotely understand what I'm trying to say... Any help at all is much appreciated!
Here's a code snippet of how the HTML is structured, maybe it'll help someone understand what I mean... Thanks again, everyone!
<div id="parent-div">
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
</div>
<div id="hidden-text">
This is the content I would like to have displayed across the three divs above.
</div>
Here is some JS that I have so far and I'm rather stuck on where to go from here:
function Populate(){
//paragraph is equal to all of the content in the hidden-text div
var paragraph = document.getElementById('hidden-text').innerHTML;
var newParagraph = "";
//the variable div would play host to the names of each of the divs in the HTML
var div = "";
//words stores each character of paragraph and passes them into the new paragraph
var words = "";
for (words in paragraph)
{
newParagraph += paragraph[words];
}
//the column with the name equal to the value of the div variable gets populated by the value of newParagraph
document.getElementByClassName(div).innerHTML = newParagraph;}
I think it should be something like this:
var divs = ['a', 'b', 'c'];
var word_limit = 50;
var paragraph_words = paragraph.split(' ');
for (var i = 0, div_index = 0; i < paragraph_words.length && div_index < divs.length; i++) {
newParagraph += ' ' + paragraph_words[i];
if (i > 0 && i % word_limit == 0) {
document.getElementsByClassName(divs[div_index])[0].innerHTML = newParagraph;
div_index++;
newParagraph = '';
}
}
if (newParagraph) { // If we didn't fill up the last DIV
document.getElementsByClassName(divs[div_index])[0].innerHTML = newParagraph;
}
i'm currently selecting divs using this:
$(".gridbox").click(function () {
$(this).toggleClass("selected");
var isSelected = $(".gridbox.selected").length > 0;
$("#button").toggle(isSelected);
});
});
Is there any way I can limit the number of selected Divs to 5? I have tried:
var isSelected1 = $(".gridbox.selected").length < 3;
if (typeof isSelected1 !== 'undefined') {
alert("You have selected the maximum amount.");
}
But that doesn't seem to work. If anyone has any ideas that'd be greatly appreciated!
Also I am presently selecting divs using:
<div onclick="clicked(this);" class="gridbox" id="1"></div>
Is there anything I can put anywhere to disable a certain ID from being selected?
Many thanks in advance!
Cheers
This seems to achieve what you are looking for:
jsFiddle example
$(".gridbox:not(#disabled)").click(function () {
if ($('.selected').length < 5 || $(this).hasClass('selected')) {
$(this).toggleClass("selected");
}
});
It will limit the selection to 5 of any elements with class .gridbox. Not just the first 5 elements.
With usage of :not, you are unable to select an element with an id of "disabled" in this example.
I was taking a look at http://www.zenfolio.com/zf/features.aspx and I can't figure out how they are doing the accordion style expand and collapse when you click on the orange links. I have been using the Web Developer Toolbar add-on for firefox, but I have not been able to find anything in the source of the page like JavaScript that would be doing the following. If anyone knows how they are doing it, that would be very helpful.
This is actually unrelated, but if all you answers are good, who do I give the answer too?
They're setting the .display CSS property on an internal DIV from 'none' to '', which renders it.
It's a bit tricky, as the JS seems to be in here that's doing it:
http://www.zenfolio.com/zf/script/en-US/mozilla5/windows/5AN2EHZJSZSGS/sitehome.js
But that's basically how everyone does this. It's in there, somewhere.
EDIT: Here it is, it looks like:
//... (bunch of junk)
zf_Features.prototype._entry_onclick = function(e, index)
{
var cellNode = this.dom().getElementsByTagName("H3")[index].parentNode;
while (cellNode.tagName != "TD") cellNode = cellNode.parentNode;
if (this._current != null) this.dom(this._current).className = "desc";
if ("i" + index != this._current)
{
cellNode.className = "desc open";
cellNode.id = this.id + "-i" + index;
this._current = "i" + index;
}
else this._current = null;
zf_frame.recalcLayout();
return false;
};
Basically, what they're doing is a really roundabout and confusing way of making the div inside of TD's with a class "desc" change to the class "desc open", which reveals its contents. So it's a really obtuse roundabout way to do the same thing everyone does (that is, handling onclick to change the display property of a hidden div to non-hidden).
EDIT 2:
lol, while I was trying to format it, others found it too. =) You're faster than I ;)
Using jQuery, this effect can be built very easily:
$('.titleToggle').click(function() {
$(this).next().toggle();
});
If you execute this code on loading the page then it will work with any markup that looks like the following:
<span class="titleToggle">Show me</span>
<div style="display:none">This is hidden</div>
Notice that this code will work for any number of elements, so even for a whole table/list full of those items, the JavaScript code does not have to be repeated or adapted in any way. The tag names (here span and div) don't matter either. Use what best suits you.
It is being done with JavaScript.
When you click a link, the parent td's class changes from 'desc' to 'desc open'. Basically, the expanded text is always there but hidden (display: none;). When it gets the css class of 'open' the text is no longer being hidden (display: block;).
If you look in the sitehome.js and sitehome.css file you can get an idea about how they are doing that.
btw I used FireBug to get that info. Even though there is some feature duplication with Web Developer Toolbar it's worth the install.
They're using javascript. You can do it too:
<div id="feature">
Feature Name
<div id="desc" style=="display:none;">
description here...
</div>
</div>
<script type="text/javascript">
function toggle()
{
var el=document.getElementById('desc');
if (el.style.display=='none')
el.style.display='block'; //show if currently hidden
else
el.style.display='none'; //Hide if currently displayed
}
</script>
The function above can be written using Jquery for smooth fade in/fade out animations when showing/expanding the descriptions. It has also got a SlideUp and Slidedown effect.
There is a lot of obfuscated/minified JS in their master JS include. It looks like they are scraping the DOM looking for H3's and checking for table cells with class desc and then processing the A tags. ( or some other order, possibly ) and then adding the onclick handlers dynamically.
if (this._current != null) this.dom(this._current).className
= "desc"; if ("i" + index != this._current) { cellNode.className = "desc open"; cellNode.id = this.id
+ "-i" + index; this._current = "i" + index; }
http://www.zenfolio.com/zf/script/en-US/safari3/windows/5AN2EHZJSZSGS/sitehome.js
The script is here.
The relevant section seems to be (re-layed out):
// This script seems over-complicated... I wouldn't recommend it!
zf_Features.prototype._init = function()
{
// Get a list of the H3 elements
var nodeList = this.dom().getElementsByTagName("H3");
// For each one...
for (var i = 0; i < nodeList.length; i++)
{
// ... set the onclick to be the function below
var onclick = this.eventHandler(this._entry_onclick, i);
// Get the first <a> within the H3 and do the same
var node = nodeList[i].getElementsByTagName("A")[0];
node.href = "#";
node.onclick = onclick;
// And again for the first <span>
node = nodeList[i].getElementsByTagName("SPAN")[0];
node.onclick = onclick;
}
};
zf_Features.prototype._entry_onclick = function(e, index)
{
// Get the parent node of the cell that was clicked on
var cellNode = this.dom().getElementsByTagName("H3")[index].parentNode;
// Keep going up the DOM tree until we find a <td>
while (cellNode.tagName != "TD")
cellNode = cellNode.parentNode;
// Collapse the currently open section if there is one
if (this._current != null)
this.dom(this._current).className = "desc";
if ("i" + index != this._current)
{
// Open the section we clicked on by changing its class
cellNode.className = "desc open";
cellNode.id = this.id + "-i" + index;
// Record this as the current one so we can close it later
this._current = "i" + index;
}
else
this._current = null;
// ???
zf_frame.recalcLayout();
return false;
};
Edit: added some comments
Unfortunately their code is in-lined and hard to read (http://www.zenfolio.com/zf/script/en-US/mozilla5/windows/5AN2EHZJSZSGS/sitehome.js), but this looks quite simple to implement... something along these lines (using prototypejs):
<script>
var showHide = {
cachedExpandable: null
,init: function() {
var containers = $$(".container");
for(var i=0, clickable; i<containers.length; i++) {
clickable = containers[i].getElementsByClassName("clickable")[0];
Event.observe(clickable, "click", function(e) {
Event.stop(e);
showHide.doIt(containers[i]);
});
}
}
,doIt: function(container) {
if(this.cachedExpandable) this.cachedExpandable.hide();
var expandable = container.getElementsByClassName("expandable")[0];
if(expandable.style.display == "none") {
expandable.show();
} else {
expandable.hide();
}
this.cachedExpandable = expandable;
}
};
window.onload = function() {
showHide.init();
};
</script>
<div class="container">
<div class="clickable">
Storage Space
</div>
<div class="expandable" style="display: none;">
Description for storage space
</div>
</div>
<div class="container">
<div class="clickable">
Galleries
</div>
<div class="expandable" style="display: none;">
Description for galleries
</div>
</div>
Its also caching the earlier expandable element, so it hides it when you click on a new one.