I have a form with input items. What I have done is get the label for each input element in my form. I have done this already but the trouble I am having is this...for every input element that does NOT have a label I want to set their label as the previous label. I did a mock up of what I am looking for in jsFiddle here.
myItems.each(function(){
label = $('label[for="' + $(this).attr('id') + '"]').text();
if(label.length <= 0){
// code I'm looking for goes here
}
});
UPDATE: I updated the JSFiddle to show a true represenstion of what I am trying to accomplish and the attempt with .prev(). JSFIDDLE LINK
Here is my solution without using jQuery.prev(), as you don't know if the previous one has a label (maybe you need the one before the prev()).
var label = '';
var myItems = $('#panel').find('input');
var previousLabel;
myItems.each(function(){
label = $('label[for="' + $(this).attr('id') + '"]').text();
if(label.length <= 0) {
// ensure that a previous label exists
if (typeof(previousLabel) != 'undefined')
{
label = previousLabel;
// Here you can use your previous label
}
} else {
previousLabel = label;
}
});
Fiddle updated
If I understand the question correctly, it would be something like this:
var label, myItems = $('#panel input');
myItems.each(function(i, e){
var l = $('[for="'+e.id+'"]');
label = l.length ? l : label;
l.length || $(e).before(label.clone().attr('for', e.id));
});
JSFiddle
Here's a solution, had to amend the HTML a little, will this work for you?
var myItems = $('#panel').find('input');
var label;
myItems.each(function(){
if($(this).parent().find('label').length){
label = $(this).parent().find('label');
} else {
var newLabel = document.createElement('label');
newLabel.innerHTML = label.html();
$(this).parent().prepend(newLabel);
}
});
http://jsfiddle.net/mp4sdfvy/10/
Related
I've known how to use the document.selection to do the highlighting. For example
/* http://jsfiddle.net/4J2dy/ */
$("#content").on('mouseup', function() {
highlighting();
});
var highlighting = function () {
var seleted_str = (document.all) ? document.selection.createRange().text : document.getSelection();
if(seleted_str != "") {
var stringToBeHighlighted = seleted_str.getRangeAt(0);
var span = document.createElement("span");
span.style.cssText = "background-color: #80deea";
span.className = "MT";
stringToBeHighlighted.surroundContents(span);
}
};
But there is something I don't know how to achieve.
Let's say that I have four layers created with the same content at the same time.
And I would like to select a sentence on the controlling layer while all the same sentence in the other three layers will be selected too.(See image below)
After the selection, I would like to pop out a menu(which I can do), and get the DOM element based on which button is pressed.(See image below)
Could anyone tell me how to achieve this? Or it just can't be done? I would be grateful if someone could answer for me.
It's kind of possible, and I would appreciate the input of SO user Tim Down as he knows a lot about JS Range/Selections, but I'll present my partial solution already.
Instead of selecting the 4 layers, you could just store the startOffset & endOffset in an external object that is updated on mouseup. The only by-effect this has, is that the user's selection will only get the color of the span when they click a layer button.
The advantage is that you can now simply work with DOM Textnodes as opposed to ranges/ selection (more complex, to me anyway).
I've chosen to find the layers with a data-layer attribute on the buttons and a corresponding id on the layers themselves. I handled the 'appending' of the 'selected span' by slicing the text content of the text nodes in the layers, like so:
layer.innerHTML = txt.slice(0, selText.start)
+ '<span class="MT" style="background-color: #80deea">'
+ txt.slice(selText.start, selText.end) + '</span>'
+ txt.slice(selText.end, txt.length);
See it in action here. I've added a cleanSelection function so only one selection is possible at a time (the start & end counters fail because selection ranges don't take into account HTML tags, so you have to get rid of the spans).
Final notes:
The fiddle will not work in browsers not supporting getElementsByClassName
The fiddle only supports one selection at a time.
The fiddle does not extensively test all conditions (eg, whether the nodetype of the first child is truly a text node, etc. But it ain't hard to add that yourself)
Entire JS code as reference (also in fiddle):
// this object will hold the start & end offsets of selection value
var selText = false;
// selText will be updated on mouseup
document.body.onmouseup = getSelText;
// on button click, selText will be highlighted
document.body.onclick = function(e) {
var target = e.target || e.srcElement, range, layer, txt;
// only do if it's a layer button & the selection is non-empty
if (target.getAttribute('data-layer') && selText !== false) {
// first remove previous spans, they break the startOffset & endOffset of the selection range
cleanSelection();
// get the clicked layer
layer = document.getElementById(target.getAttribute('data-layer'));
// this assumes that the first node in the layer is a textNode
txt = layer.firstChild.nodeValue;
// let's append the selection container now
layer.innerHTML = txt.slice(0, selText.start)
+ '<span class="MT" style="background-color: #80deea">'
+ txt.slice(selText.start, selText.end) + '</span>'
+ txt.slice(selText.end, txt.length);
// ...and empty the 'real selection'
window.getSelection().collapse();
// log results to console
console.log('From char ' + selText.start + ' to char ' + selText.end + ', in ' + layer.id);
}
};
function getSelText () {
var seleted_str = (document.all) ? document.selection.createRange().text : document.getSelection(), stringToBeHighlighted;
if(seleted_str !== "") {
stringToBeHighlighted = seleted_str.getRangeAt(0);
selText = {
start: stringToBeHighlighted.startOffset,
end: stringToBeHighlighted.endOffset
};
} else {
selText = false;
}
}
function cleanSelection() {
var getText, mtSpan = document.getElementsByClassName('MT');
for ( var i = 0; i < mtSpan.length; i++) {
getText = mtSpan[i].innerHTML;
mtSpan[i].previousSibling.nodeValue = mtSpan[i].previousSibling.nodeValue + getText + mtSpan[i].nextSibling.nodeValue;
mtSpan[i].parentNode.removeChild(mtSpan[i].nextSibling);
mtSpan[i].parentNode.removeChild(mtSpan[i]);
}
}
This issue is making me want to smash my computer. I have a form, I want to calculate values from selections on the form. I have two jsfiddles here, I really want to have it so I can make selections, then calculate on click. But I can't even get the form to work on click. So the other fiddle uses on change and keyup functions. There is an issue on that form as well. If you change the first select to "option 2", you'll see the value for that select ends up being "1.379999999996" instead of "1.38". Why is this happening?
Fiddle with click function
JS:
$('#submit').click(function(){
var price=$(this).find("option:selected").attr('data-price');
var ink=$('#ink').val();
var loc1=$('#loc1').val();
var res= price*1 + ink*1 + loc1*1 ;
$('#bleh').val( res || 0 );
});
Fiddle with change and keyup functions
JS:
$('#ink, #loc1, .sel').on('keyup change',function(){
var price=$('option:selected', this).attr('data-price');
var ink=$('#ink').val();
var loc1=$('#loc1').val();
var res= price*1 + ink*1 + loc1*1 ;
$('#bleh').val( res || 0 );
});
Your selector $(this).find("option:selected") is wrong as this here points to the #submit button, so you need to find out the select element using its id #style
$('#submit').click(function(){
var price=$('#style').find("option:selected").attr('data-price');
var ink=$('#ink').val();
var loc1=$('#loc1').val();
var res= price*1 + ink*1 + loc1*1 ;
$('#bleh').val( res || 0 );
});
Demo: Fiddle
Looking for some help on how to write a function to filter out certain divs with certain classes.
Essentially I have thrown together a quick e-commerce example. There are lists of different filters, with values. There are then products. Each product div has a number of classes applied to it, e.g "green" or "adult" or "wool" - these are the filterable parameters.
Not being savvy at all with JS I'm trying to write something, but looking for some advice. Here is basically what I'm after:
Starts with displaying all
If user selects GREEN, all items that do not have GREEN attributed are display:none'd (with a fade transition
Rep #2 for any attribute checked
Notes: multiple attributes can be checked, when items are unchecked, everything needs to reappear.
Any help? I guess it's basically linking up the value of each checkbox to the class.
Not sure if there is a better way codewise to do this... data attributes maybe?
Working example of the code here (obviously no JS)
Updated your fiddle and added some jQuery to hide the divs where the classes don't match the selected checkboxes.
Demo: fiddle
JS is a bit verbose, you can refactor it further if you like:
$(document).ready(function() {
var allSelectedClasses;
allSelectedClasses = '';
$('input[type="checkbox"]').click(function(){
//ensure the correct classes are added to the running list
if(this.checked){
allSelectedClasses += '.' + $(this).val();
}else{
allSelectedClasses = allSelectedClasses.replace($(this).val(), '');
}
//format the list of classes
allSelectedClasses = allSelectedClasses.replace(' ', '');
allSelectedClasses = allSelectedClasses.replace('..', '.');
var selectedClasses;
var allSelected;
allSelected = '';
//format these for the jquery selector
selectedClasses = allSelectedClasses.split(".");
for(var i=0;i < selectedClasses.length;i++){
var item = selectedClasses[i];
if(item.length > 0){
if(allSelected.length == 0){
allSelected += '.' + item;
}else{
allSelected += ', .' + item;
}
}
}
//show all divs by default
$("div.prodGrid > div").show();
//hide the necessary ones, include the 2 top level divs to prevent them hiding as well
if(allSelected.length > 0){
$("div.prodGrid > div:not(" + allSelected + ")").hide();
}
});
});
I added a new class to your Colors ul. Hope that's okay.
Here's a crude version of a filtering function, it only takes colors into account so you have to modify it yourself to take everything into account but the basic outline is there.
It can be refactored massively! :)
Since you're using jQuery:
$('ul.colorFilter li input[type="checkbox"]').click(function(){
var checkedBoxes = $('ul.colorFilter li input[type="checkbox"]:checked');
var listOfClasses = [];
checkedBoxes.each(function(index, el){
listOfClasses.push(el.value);
});
if(listOfClasses.length >= 1){
$('div.prodGrid').children('div').hide();
for(var i = 0; i < listOfClasses.length; i++){
$('div.prodGrid > div.'+listOfClasses[i]).show();
}
} else {
$('div.prodGrid > div').show();
}
});
I made a fiddle as well:
http://jsfiddle.net/Z9ZVk/4/
The below post was hugely helpful for what I'm trying to do:
Builing a cart with multiple items and the PayPal button in PHP
This part works beautifully:
var inp1 = document.createElement("input");
inp1.setAttribute("type", "hidden");
inp1.setAttribute("id", "item_number_" + current_item);
inp1.setAttribute("name", "item_number_" + current_item);
inp1.setAttribute("value", current_item);
document.getElementById("paypal-form").appendChild(inp1);
but I'm getting a bit stuck on how to remove an item when needed... I am looking for something like:
document.getElementById('payPalForm').removeChild(inp1);
But obviously I need a way to specify/track those dynamically created id's... or is there an easier way I'm missing?
Any help would be appreciated.
use the parentNode property to retrieve the parent element of an element, and use the removeChild method of the parent:
inp1.parentNode.removeChild (inp1);
Reference:
parentNode property
I'm not sure where this code belongs, but it would be helpful to encapsulate this within a class.
function NewClass() {
var formPP = document.getElementById('payPalForm');
var lInputs = [];
this.addItem = function(val) {
var inp1 = document.createElement("input");
inp1.setAttribute("type", "hidden");
inp1.setAttribute("id", "item_number_" + val);
inp1.setAttribute("name", "item_number_" + val);
inp1.setAttribute("value", val);
formPP.appendChild(inp1);
lInputs.push(inp1);
}
this.removeItem = function(val) {
var e = formPP.firstChild;
while( e ) {
var eNext = e.nextSibling;
if( e.value == val )
formPP.removeChild(e);
e = eNext;
}
}
}
You'll have to tailor this to meet your needs, but hopefully this gives you a start.
I use this code to determine checkbox width and height:
var chk = $('input[type="checkbox"]:first');
chkWidth = chk.innerWidth()
+ parseInt(chk.css('margin-left').replace('px', ''))
+ parseInt(chk.css('margin-right').replace('px', '')); /*IS there better way instead of px replace?? */
chkHeight = chk.innerHeight()
+ parseInt(chk.css('margin-top').replace('px', ''))
+ parseInt(chk.css('margin-bottom').replace('px', ''));
fieldWidth = gamefield.innerWidth();
fieldHeight = gamefield.innerHeight();
Then i try to fill div with the number of checkboxes that exactly fits given div. In fact i get a lot of checkboxes out of div.
http://dl.dropbox.com/u/3473965/jq/tetris.html
http://dl.dropbox.com/u/3473965/jq/tetris.js
Is there anyway to solve this? Thank you!
leaving other things as such, this makes the difference (on FF3.5):
var cols=Math.floor(fieldWidth/chkWidth);
var rows=Math.floor(fieldHeight/chkHeight);
var html='';
for(var i=0;i<=rows;i++) //if yo use i<rows there is space for one more row
{
for(var j=0;j<cols;j++)
{
html +="<input type='checkbox'/>";
}
}
gamefield.html(html);