问题

Then if those new boxes are checked another 3 checkboxes would show for that level.

habits/_form.html.erb
<label id="<%= @habit.id %>" class="habit-id"> Missed: </label>
<% @habit.levels.each_with_index do |level, index| %>
<% if @habit.current_level >= (index + 1) %>
<p>
<label id="<%= level.id %>" class="level-id"> Level <%= index + 1 %>: </label>
<%= check_box_tag nil, true, level.missed_days > 0, {class: "habit-check"} %>
<%= check_box_tag nil, true, level.missed_days > 1, {class: "habit-check"} %>
<%= check_box_tag nil, true, level.missed_days > 2, {class: "habit-check"} %>
</p>
<% end %>
<% end %>
habit.js
$(document).ready(function()
{
$(".habit-check").change(function()
{
habit = $(this).parent().siblings(".habit-id").first().attr("id");
level = $(this).siblings(".level-id").first().attr("id");
if($(this).is(":checked"))
{
$.ajax(
{
url: "/habits/" + habit + "/levels/" + level + "/days_missed",
method: "POST"
});
}
else
{
$.ajax(
{
url: "/habits/" + habit + "/levels/" + level + "/days_missed/1",
method: "DELETE"
});
}
});
});
habit.rb
class Habit < ActiveRecord::Base
belongs_to :user
has_many :comments, as: :commentable
has_many :levels
serialize :committed, Array
validates :date_started, presence: true
before_save :current_level
acts_as_taggable
scope :private_submit, -> { where(private_submit: true) }
scope :public_submit, -> { where(private_submit: false) }
attr_accessor :missed_one, :missed_two, :missed_three
def save_with_current_level
self.levels.build
self.levels.build
self.levels.build
self.levels.build
self.levels.build
self.save
end
def self.committed_for_today
today_name = Date::DAYNAMES[Date.today.wday].downcase
ids = all.select { |h| h.committed.include? today_name }.map(&:id)
where(id: ids)
end
def current_level_strike
levels[current_level - 1] # remember arrays indexes start at 0
end
def current_level
return 0 unless date_started
def committed_wdays
committed.map do |day|
Date::DAYNAMES.index(day.titleize)
end
end
def n_days
((date_started.to_date)..Date.today).count do |date|
committed_wdays.include? date.wday
end - self.missed_days - self.days_lost
end
case n_days
when 0..9
1
when 10..24
2
when 25..44
3
when 45..69
4
when 70..99
5
else
6
end
end
end
days_missed_controller
class DaysMissedController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
habit = Habit.find(params[:habit_id])
habit.missed_days = habit.missed_days + 1
habit.save!
level = habit.levels.find(params[:level_id])
level.missed_days = level.missed_days + 1
level.save!
head :ok # this returns an empty response with a 200 success status code
if missed_days == 3
missed_days = 0
days_lost += pending_days
pending_days += 1
pending_days = 0
end
end
def destroy
habit = Habit.find(params[:habit_id])
habit.missed_days = habit.missed_days - 1
habit.save
level = habit.levels.find(params[:level_id])
level.missed_days = level.missed_days - 1
level.save!
head :ok # this returns an empty response with a 200 success status code
end
end
If you need further explanation, code, or pictures please don't hesitate to ask, you can also find at your discretion my additional code here: https://gist.github.com/RallyWithGalli/c66dee6dfb9ab5d338c2
Thank you!
回答1:
Attach a handler for the change
event to each check box which counts how many input elements with the attribute type="checkbox"
which are :not(:checked)
there are. If there are no unchecked boxes, add three boxes and attach the same click event handler to each new box. Every time all of the boxes are filled it will add three new ones.
This is the relevant logic part of the script
if(!element.querySelectorAll('input[type="checkbox"]:not(:checked)').length) {
/* add three checkboxes */
}
If there are no input fields of type checkbox which do not have the status checked, the length property will be undefined and resolve to false. If there are input field which match the previously stated criteria, the length property will be a number value of 1 or higher and will resolve to true.
Example
var boxWrap = document.querySelector('.boxes');
var handleChange = function() {
if (!boxWrap.querySelectorAll('input[type="checkbox"]:not(:checked)').length) {
addBoxes(3);
}
};
var addBoxes = function(n) {
for (i = 0; i < n; i++) {
var box = document.createElement('input');
box.type = 'checkbox';
box.addEventListener('change', handleChange, false);
boxWrap.appendChild(box);
}
};
boxes = boxWrap.querySelectorAll('input[type="checkbox"]');
for (var i = 0; i < boxes.length; i++) {
boxes[i].addEventListener('change', handleChange, false);
}
<div class="boxes">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
</div>
EDIT
I've changed a couple of things in your original script
Improved formatting
Changed the way that you find the level-id and habit-id to reduce complexity.
I've moved the on change function into a named handler function so that it can be applied to the new checkboxes.
Added the logic described above to your onchange event capture.
I've included a basic example of how to add the new checkboxes, you will need to change the html and add logic to determine what the attributes should be.
$(document).ready(function() {
var handleChange = function() {
habit = $(this).parent().prev().attr("id");
level = $('label', $(this).parent()).attr("id");
if ($(this).is(":checked")) {
$.ajax({
url: "/habits/" + habit + "/levels/" + level + "/days_missed",
method: "POST"
});
} else {
$.ajax({
url: "/habits/" + habit + "/levels/" + level + "/days_missed/1",
method: "DELETE"
});
}
if (!$('input[type="checkbox"]:not(:checked)', $(this).parent()).length) {
/* this is just an example, you will have to ammend this */
$(this).parent().append($('<input type="checkbox" class="habit-check">'));
$(this).parent().append($('<input type="checkbox" class="habit-check">'));
$(this).parent().append($('<input type="checkbox" class="habit-check">'));
$(".habit-check").on('change',handleChange);
}
}
$(".habit-check").on('change',handleChange);
});
来源:https://stackoverflow.com/questions/30109624/how-to-call-3-new-checkboxes-upon-checking-3-old-checkboxes