Skip to content

События «mouseover» и «mouseout» для многослойных html-конструкций

Наверное многие программеры сталкивались с такой фигней, когда надо повесить, например, анимацию на кнопку, которая состоит из нескольких слоев (включая текстовый заголовок). Если слои вложены друг в друга по простому, без всяких position:relative и position:absolute, то делается это довольно-таки просто. Но как быть, если кнопка имеет неопределенную форму, и дочерние слои, например, вылазят за границы кнопки?

Самый простой метод, без выноса мозга, это перекрыть все слои одним верхним, прозрачным, и на него повесть все события. И в большинстве вариантов верстки таких кнопок это проходит на ура.. Но, все-таки, бывают случаи, когда «эстетическая» красота интерфейса очень важна. Например, ссылка под кнопкой должна откликаться отдельно (как ссылка), но в то же время подсвечивать всю кнопку.

Превью скрипта и полный исходник примера под катом.

Скачать полный пример

var f = function() {
	var items = [];
	/*  ---------- place your code below -------------- */
	/*  --------- and replace this example ------------ */

	//here construct the array with your buttons
	items.push({button: document.getElementById("my-hover-btn")});
	//---------------------------
	var item;
	//here find and push to this array elements for your animation
	for (var c in items) {
		if (!items.hasOwnProperty(c)) continue;
		item = items[c];
		item.bg = $(item.button).find(".bg")[0];
		item.link = $(item.button).find(".title")[0];
		//here define the callback function for your animation which will handle
		//"on", "off" and "stop" conditions and pass doneCb to jQuery
		item.animate = function(cond, doneCb) {
			switch(cond) {
				case "on":
					$(item.bg).animate({opacity: 1}, 300, doneCb);
					$(item.link).animate({color: "#f26522"}, 300);
					break;
				case "off":
					$(item.bg).animate({opacity: 0}, 300, doneCb);
					$(item.link).animate({color: "#000"}, 300);
					break;
				default://stop
					$(item.bg).stop(true, true);
					$(item.link).stop(true, true);
			}
		};
	}

	/*  ------------------------------------------------ */
	return items;
};
(function(f) {
	var ct = 0;//current try
	var mt = 200;//max try count
	var it = 300;//retry timeout
	var jc = "jquery/jquery.color.js";
	var jq = "jquery/jquery-1.10.2.min.js";
	var h = document.getElementsByTagName("HEAD")[0];
	var i =	window.setInterval(function() {
		ct++;
		if (ct > mt) {
			window.clearInterval(i);
			return
		}
		var r;
		if (typeof $ == "undefined") {
			if (jq) {
				r = document.createElement("SCRIPT");
				r.type = "text/javascript";
				r.async = true;
				r.src = jq;
				h.appendChild(r);
				jq = "";
			}
			return;
		}
		if (jc) {
			for (var c in h.childNodes) {
				if ((typeof h.childNodes[c].tagName != "undefined") && (h.childNodes[c].tagName == "SCRIPT") && h.childNodes[c].src) {
					if (h.childNodes[c].src.indexOf("jquery.color.js") != -1) {
						jc = "";
						break;
					}
				}
			}
			if (!jc) return;
			r = document.createElement("SCRIPT");
			r.type = "text/javascript";
			r.async = true;
			r.src = jc;
			h.appendChild(r);
			jc = "";
			return;
		}
		window.clearInterval(i);
		//snippet worker
		var proc = function(items) {
			var apply = function(item, cond) {
				if (cond == "on") {
					if ((item.state == 1) || (item.state == 2)) return;
					if (item.state == 3) item.animate("");
					item.animate("on", item.done);
					item.state = 1;
				} else {
					if ((item.state == 0) || (item.state == 3)) return;
					if (item.state == 1) item.animate("");
					item.animate("off", item.done);
					item.state = 3;
				}
			};
			var done = function() {
				this.state = 3 - this.state;
			};
			var fover = function(item) {
				var rel = arguments[1].relatedTarget ? arguments[1].relatedTarget : arguments[1].fromElement;
				if (rel && isButton(rel, item.button)) return;
				apply(item, "on");
			};
			var fout = function(item) {
				var rel = arguments[1].relatedTarget ? arguments[1].relatedTarget : arguments[1].toElement;
				if (rel && isButton(rel, item.button)) return;
				apply(item, "off");
			};
			var isButton = function(node, parent) {
				if (typeof node != "object" || (typeof node.parentNode == "undefined")) return false;
				if ((typeof parent != "object") || (typeof node.childNodes == "undefined")) return false;
				var res = $(parent).find(node);
				if (typeof res[0] != "undefined") return true;
				return false;
			};
			var start = function(items) {
				for (var c in items) {
					if (!items.hasOwnProperty(c)) continue;
					var item = items[c];
					$(item.button).mouseover(fover.bind(fover, item));
					$(item.button).mouseout(fout.bind(fout, item));
					item.done = done.bind(item);
					item.state = 0;
				}
			};
			start(items);
		};
		proc(f());
	}, 300);
})(f);