Javascript-форум (https://javascript.ru/forum/)
-   jQuery (https://javascript.ru/forum/jquery/)
-   -   Обработчик события действует только на ссылки? (https://javascript.ru/forum/jquery/11039-obrabotchik-sobytiya-dejjstvuet-tolko-na-ssylki.html)

Cuprum 03.08.2010 20:09

Обработчик события действует только на ссылки?
 
Есть незамысловатый код ((document).ready подразумевается):
var textSelection = ''; 
function getSel() { //функция пишет в переменную выделение мышкой
	if (textSelection = window.getSelection)	// !IE
		textSelection = window.getSelection().toString();
	else	// IE
		textSelection = document.selection.createRange().text;
	return textSelection;
}
$('body').mouseup(function(){
		getSel();		
});
$('#link').click(function () {
		alert (textSelection);
});

Проблема вот в чем: почему то обработчик работает как-то не так (а может я чего-то не знаю:-? ). Если элемент это ссылка с id="link", то все в порядке, в модальном окне виден выделенный текст. Но если элемент не является ссылкой (например <div id="link">, <span id="link"> и т.д.) то появляется просто пустое модальное окно.
Вопросы просты: как заставить обрабочик выводить выделенный фрагмент по клику на блоке (нужно именно это) и где ошибка в приведенном коде?

рони 04.08.2010 22:06

1.
if (textSelection = window.getSelection)
должна быть
if (window.getSelection)

2. так как click состоит из mousedown, mouseup $('#link').click стирает предыдущее выделение, если выделение было на другом элементе кроме #link'
надо менять логику скрипта или как вариант сделать так:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title></title>
</head>

<body><div id="link">Выдели текст и кликни тут!!!</div><div id="link0">всякий разный текст</div><div id="link1">всякий разный текст</div><div id="link2">всякий разный текст</div>

<script language="JavaScript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script language="JavaScript" type="text/javascript">
var textSelection = '';

function getSel() { //функция пишет в переменную выделение мышкой

    if (window.getSelection) // !IE
    textSelection = window.getSelection().toString();
    else // IE
    textSelection = document.selection.createRange().text;
    return textSelection;
}
$('body').mouseup(function () {
    getSel();
});
$('#link').mouseup(function () {
    if (textSelection) alert(textSelection);
});
</script>
</body>

</html>

Cuprum 04.08.2010 22:59

Спасибо за код и инфу, по поводу затирания не знал. А если как-то в моем выражении
$('body').mouseup(function(){
	        getSel();      
});

докапаться до того элемента где непосредственно происходит событие и работать уже с ним? Это спасет? Те "слушать" весь body, но при отпускании мыши работать с тем элементом, над которым произошел mouseup.

Octane 04.08.2010 23:57

Цитата:

Сообщение от рони
1.
if (textSelection = window.getSelection)

должна быть
if (window.getSelection)

Здесь нет ошибки.


Я бы сделал как-то так:
$Selection = {
	standardsCompliant: typeof getSelection != "undefined",
	get: function () {
		return this.standardsCompliant ? getSelection() : document.selection;
	},
	getRange: function () {
		return this.get()[this.standardsCompliant ? "getRangeAt" : "createRange"](0);
	}
};

$Range = {
	standardsCompliant: $Selection.standardsCompliant,
	stringify: function (range) {
		return this.standardsCompliant ? range.toString() : range.text;
	}
};

var text;

$("body").mouseup(function () {
	text = $Range.stringify($Selection.getRange());
});

$("#link").click(function () {
	alert(text);
});

Cuprum 05.08.2010 00:34

Octane,
Цитата:

Здесь нет ошибки
Зато чуть покороче,:)

А в чем преимущество вашего способа определения выделения (сложновато будет;)) перед функцией?
ЗЫ. $Selection - это объект, не так ли?

x-yuri 05.08.2010 00:38

ну если продолжать, то я бы избавился от глобальной переменной и подготовил пути к развитию
Function.prototype.bind = function(scope) {
    var that = this, args = [].slice.call(arguments, 1);
    return function() {
        return that.apply(scope || this, args.concat([].slice.call(arguments, 0)));
    };
};

function QuoteLink( el ){
    this._el = $(el);
    $("body").mouseup( this._onMouseUp_body.bind(this) );
    this._el.click( this._onClick.bind(this) );
}

QuoteLink.prototype._onMouseUp_body = function(){
    this._text = $Range.stringify($Selection.getRange());
}

QuoteLink.prototype._onClick = function(){
    alert(this._text);
}

x-yuri 05.08.2010 00:39

Цитата:

Сообщение от Cuprum
Зато чуть покороче,

и что, что короче?

Cuprum 05.08.2010 00:50

Цитата:

Сообщение от x-yuri (Сообщение 66353)
и что, что короче?

Если можно сделать проще и это работает, зачем городить огород? (В принципе, не только применительно к коду.)

Octane 05.08.2010 01:08

Ой самое главное забыл дописать. Чтобы ваш вариант работал, нужно preventDefault выполнить по mousedown на #link.
<!DOCTYPE html>
<meta charset="utf-8">
<div>
	<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
	<span id="test">test</span>
</div>
<script>
$Selection = {
	standardsCompliant: typeof getSelection != "undefined",
	get: function () {
		return this.standardsCompliant ? getSelection() : document.selection;
	},
	getRange: function () {
		return this.get()[this.standardsCompliant ? "getRangeAt" : "createRange"](0);
	}
};

$Range = {
	standardsCompliant: $Selection.standardsCompliant,
	stringify: function (range) {
		return this.standardsCompliant ? range.toString() : range.text;
	}
};

//~~~~~~~~~~~~~~~~~~~~~~~

var text;

document.body.onmouseup = function () {
	text = $Range.stringify($Selection.getRange());
};

var btn = document.getElementById("test");

btn.onmousedown = function (event) {
	if (event && event.preventDefault) {
		event.preventDefault();
	} else {
		event.returnValue = false;
	}
};

btn.onclick = function () {
	alert(text);
};
</script>


Цитата:

Сообщение от Cuprum
ЗЫ. $Selection - это объект, не так ли?

Да обычный объект.

Цитата:

Сообщение от Cuprum
А в чем преимущество вашего способа определения выделения (сложновато будет) перед функцией?

Ничего в этом коде сложного нет. Преимущество в том, что кросс-браузерная работа с Selection/Range/TextRange вынесена из тела функции и эти методы можно будет использовать где-то еще. В таком коде легче разобраться.

Короче не всегда лучше.

x-yuri 05.08.2010 01:16

проще понятие неоднозначное и уж точно не то же самое, что и "меньше строк". Меньше строк получилось? Отлично. А строки от этого проще стали? Пихать все в одну строку - усложнение кода (понимания кода). В частности, использование выражений с побочными эффектами. Разве не проще рассчитывать, что все выражения будут без побочных эффектов? Для меня же проще в первую очередь, когда я вижу что происходит, а не как это реализовано (это в целом про "что такое просто для меня"). Да и Эйнштейн, ведь, говорил про "настолько просто, насколько это возможно", а не про "максимально просто"

Вариант Octane - это выделение библиотечного кода, который будет повторно использоваться. Мой вариант - выделение компонентов, которые тоже будут повторно использоваться. Для кода в первом сообщении это, в общем-то, не актуально, но ведь это не окончательный вариант?

Cuprum 05.08.2010 02:08

x-yuri,
Да, я в первом сообщении не упомянул (и хорошо), что вряд ли код (функция выделения фрагмента) будет где-то еще использоваться. Функция будет является частью небольшого скрипта и действовать только в его рамках.
Цитата:

Сообщение от x-yuri
В частности, использование выражений с побочными эффектами.

Намекаете, что у функции есть побочные эффекты? Можно поподробнее? Интересно с т. з. получения нового знания.

Цитата:

Сообщение от x-yuri
когда я вижу что происходит, а не как это реализовано

Глядя на ваш пример и Octane я, увы, не могу сказать что я вижу (конечно, дело во мне).
И "простоту" я упомянул зря - она не только не однозначна, но как оказалось, ситуативна :yes: .

x-yuri 05.08.2010 04:17

Цитата:

Сообщение от Cuprum
Функция будет является частью небольшого скрипта и действовать только в его рамках

ключевые слова "частью небольшого скрипта". Хотелось бы увидеть этот небольшой скрипт...

Цитата:

Сообщение от Cuprum
Намекаете, что у функции есть побочные эффекты? Можно поподробнее? Интересно с т. з. получения нового знания.

выражения с побочными эффектами - выражения, в которых не только что-то вычисляется, но и что то изменяется. В первую очередь это всякие ++, --. Согласитесь, что проще рассчитывать, что в if у меня только проверяется условие и ничего не меняется. А ведь условие может быть не таким простым. Кроме того, не понятно, зачем вашей функции глобальная переменная. Вроде как по названию складывается впечатление, что она ничего не меняет, а только возвращает. В результате ее проще (и в вашем и в моем понимании) записать так:
function getSel(){
    if( window.getSelection )
        return window.getSelection();
    else
        return document.selection.createRange().text;
}


Цитата:

Сообщение от Cuprum
Глядя на ваш пример и Octane я, увы, не могу сказать что я вижу (конечно, дело во мне).

ну видимо смотрите не с той стороны ;) Я рассчитвал, что нужно сделать некий компонент "ссылка для цитирования", т.е. я встрачаю в коде
<a id="quote-link" href="#">Цитировать</a>
<script type="text/javascript">new QuoteLink('#quote-link')</script>

и вижу, что при щелчке по этой ссылке происходит копирование выделенного текста (так как не было указано куда, соответственно компонент не доделан). Т.е. я вижу, что происходит, но не вижу как это реализовано. Если мне нужно узнать, как это реализовано я смотрю исходник компонента
function QuoteLink( el ){ 
    this._el = $(el); 
    $("body").mouseup( this._onMouseUp_body.bind(this) ); 
    this._el.click( this._onClick.bind(this) ); 
} 
  
QuoteLink.prototype._onMouseUp_body = function(){ 
    this._text = $Range.stringify($Selection.getRange()); 
} 
  
QuoteLink.prototype._onClick = function(){ 
    alert(this._text); 
}

и вижу, что назначаются два обработчика, что происходит в обработчиках, но не вижу реализацию назначения обработчиков, как находится выделение. Т.е. на каждом уровне у меня есть минимум необходимых деталей. В общем-то я не знаю вашей задачи, поэтому какие-то действия могут быть лишними или может быть лучший вариант организации кода, но вынесение библиотечного кода в любом случае не помешает

Кроме того, в моем случае нету никаких необоснованно глобальных переменных.

Вообще можно было еще всякие детали поубирать, вот только jQuery не предоставляет такие возможности. Хотя по задумке и не должна. Все должно быть максимально просто. Просто пишем код и пишем, и никаких дополнительных движений не делаем. Зачем разбивать код на вменяемые части? Ведь это дополнительные движения, сложно. Зачем повторное использование? Лучше мы в каждом плагине напишем то же самое. Зачем нам компоненты, если можно общаться с объектами через одну функцию ($(foo).dialog('open'))?

Цитата:

Сообщение от Cuprum
И "простоту" я упомянул зря - она не только не однозначна, но как оказалось, ситуативна

а в чем ситуативность?

Cuprum 05.08.2010 12:26

Цитата:

Сообщение от x-yuri
ключевые слова "частью небольшого скрипта"

Ключевое слово - будет. Скрипт есть
, но пока работает не совсем так, и делает еще не все, что я задумал.
Цитата:

Сообщение от x-yuri
Хотелось бы увидеть этот небольшой скрипт

Не проблема, в конце сообщения покажу.
Цитата:

Сообщение от x-yuri
ну видимо смотрите не с той стороны

Согласен, в том смысле, что js я знаю плохо. Поэтому
Цитата:

Сообщение от x-yuri
Просто пишем код и пишем, и никаких дополнительных движений не делаем :dance:

Цитата:

Сообщение от x-yuri
а в чем ситуативность?

в одном случае, когда есть какие-то описательные параметры, сделать проще - сделать одним способом, если эти параметры изменились или отсутствуют, "проще" - значит по-другому.

Сам скрипт (условно назвал "просто вставить цитату" - видел на некоторых форумах, вот пытаюсь сделать что-то подобное)
// Задаем необходимые переменные
	var qPanel = $('<div id="past" style="position:absolute; display:none;"><a href="" id="link">Вставить цитату в поле<a></div>');
	var textarea = $('textarea'); 

	function getSel() { // Функция возвращает текстовое выделение
		if (window.getSelection)	// !IE, используем метод getSelection
			return window.getSelection().toString();
		else	// IE, используем объект selection
			return document.selection.createRange().text;
	}

	qPanel.click(function () { // Обработчик события 
		textarea.val(textarea.val() + '<blockquote>' + getSel() + '</blockquote>\r\n').focus();
		$(this).hide();
		return false;
	});

	qPanel.appendTo('body');

	$('body').mouseup(function(event){
		var widthqPanel = qPanel.outerWidth();
		if (getSel())
		{
			qPanel
			.css({
				top: event.pageY - 40,
				left: event.pageX - widthqPanel/3,
				display: 'block',
				opacity: 0
			})
			.animate({
				top: '-=' + 10 + 'px',
				opacity: 1
			}, 250);
			}
	});
	$('body').click(function () { 
		if (!getSel()) qPanel.hide();
	});

x-yuri 05.08.2010 22:17

Цитата:

Сообщение от Cuprum
Согласен, в том смысле, что js я знаю плохо

не думаю я, что это не от языка зависит. Просто в некоторый языках принято все слишком упрощать, а в некоторых - слишком усложнять (ну или нельзя не усложнять) :)

Цитата:

Сообщение от Cuprum
в одном случае, когда есть какие-то описательные параметры, сделать проще - сделать одним способом, если эти параметры изменились или отсутствуют, "проще" - значит по-другому.

ну да, логично. Решение зависит от контекста. Чем проще, тем лучше. Проще зависит от контекста

я бы сделал как-то так (ошибки не исправлял, может даже новые добавил)
var QuotePanel = new Class({
    initialize: function( textarea ){
        this._el = textarea;
        this._panel = new Element('div', {'html': '<a href="#">Вставить цитату в поле</a>'});
		document.body.addEvents({
			'mouseup': this._onMouseUp_body.of(this),
			'click': this._onClick_body.of(this)
		});
		this._panel.addEvent( 'click', this._onClick_panel.of(this) );
		this._panel.inject( document.body );
    },
	
	_onMouseUp_body: function( e ){
		if( ! Selection.get() )
			return;
		var panelWidth = this._panel.getStyle('width').parseInt();
		var top = e.page.y - 40;
		var left = e.page.x - panelWidth/3;
		this._panel
			.setStyles({
				'top': top,
				'left': left,
				'display': 'block',
				'opacity': 0
			})
			.morph({
				'opacity': 1,
				'top': top-10
			});
	},
	
	_onClick_body: function( e ){
		if( ! Selection.get() )
			this._panel.setStyle('display', '');
	},
	
	_onClick_panel: function( e ){
		this._el.value += this._el.value+'<blockquote>'+Selection.get()+'</blockquote>\r\n';
		this._el.focus();
		this._panel.setStyle('display', '');
		e.preventDefault();
	}
});

Cuprum 06.08.2010 00:29

x-yuri,
Ловко вы перешли на MooTools! :) Там, где скрипт будет использоваться есть только jQuery, так что придется остаться на нем.
Возник вопрос - (e) это сокращенная форма записи (event)?
И еще один - долго ломал над ним голову и так и не придумал ничего путного. Хочу на панельку, всплывающую после выделения мышкой, повесить таймер - если, допустим, 3 секунды ничего не происходит (нет клика на ней и вне ее), панель исчезает. Если до истечения этих 3 секунд был клик вне панели, панель тут же исчезает (если был клик на панели, цитата вставляется в поле ответа). Может подскажите как это реализовать?

JsLoveR 06.08.2010 00:46

Цитата:

Сообщение от Cuprum
(e) это сокращенная форма записи (event)?

event передается аргументом в функцию-обработчик.
Цитата:

Сообщение от Cuprum
повесить таймер - если, допустим, 3 секунды ничего не происходит

с setTimeout, setInterval знакомы ?

Cuprum 06.08.2010 00:59

Цитата:

Сообщение от JsLoveR
с setTimeout, setInterval знакомы ?

Шапочно знаком, но тут как-то все не однозначно - т. е. пока эти 3 секунды не истекли и панель не исчезла (функция в теле setTimeout) надо еще и следить был ли внешний клик...

x-yuri 06.08.2010 01:45

Цитата:

Сообщение от Cuprum
Ловко вы перешли на MooTools

на самом деле не все так грустно. Просто jQuery в лучшем случае оставляет проблемы организации кода на плечах разработчиков. В худшем провоцирует то, что мы видим в исходниках плагинов. Была тут тема недавно. Кроме того, у mootools есть builder, который позволяет взять только нужную функциональность. Кроме того есть готовые решения того, как можно добавить ООП в jQuery. Ну и помимо всего прочего для данного случая просто и свой велосипед написать
function Class( methods ){
    var r = function(){
        if( this.initialize )
            this.initialize();
    };
    $.extend( r.prototype, methods );
    return r;
}

String.prototype.parseInt = function(){
    return parseInt(this);
};

Function.prototype.of = function( o ){
    var self = this;
    return function(){
        return self.apply(o, arguments);
    }
};

var QuotePanel = new Class({ 
    initialize: function( textarea ){ 
        this._el = $(textarea); 
        this._panel = $('<div><a href="#">Вставить цитату в поле</a></div>'); 
        document.body.mouseup( this._onMouseUp_body.of(this) );
        document.body.click( this._onClick_body.of(this) );
        this._panel.click( this._onClick_panel.of(this) );
        this._panel.appendTo( $('body') );
    }, 
      
    _onMouseUp_body: function( e ){ 
        if( ! Selection.get() ) 
            return; 
        var panelWidth = this._panel.css('width').parseInt(); 
        this._panel 
            .css({ 
                'top': e.pageY - 40, 
                'left': e.pageX - panelWidth/3, 
                'display': 'block', 
                'opacity': 0 
            }) 
            .animate({ 
                'opacity': 1, 
                'top': '-='+10+'px' 
            });
    },
      
    _onClick_body: function( e ){ 
        if( ! Selection.get() ) 
            this._panel.css('display', ''); 
    }, 
      
    _onClick_panel: function( e ){ 
        this._el.value += this._el.value+'<blockquote>'+Selection.get()+'</blockquote>\r\n'; 
        this._el.focus(); 
        this._panel.css('display', ''); 
        e.preventDefault(); 
    } 
});

да, я, кстати, подразумевал, что лишний css, который не изменяется я бы поместил в отдельный файл. Это несколько усложняет использование, но упрощает код

Цитата:

Сообщение от Cuprum
И еще один - долго ломал над ним голову и так и не придумал ничего путного

так совсем же просто
var QuotePanel = new Class({ 
    _onMouseUp_body: function( e ){ 
        ...
        this._panel 
            ...
            .animate(
                { ... },
                {
                    'complete': function(){
                        setTimeout( function(){
                            this._hidePanel();
                        }.of(this), 3000 );
                    }.of(this)
                });
    },
      
    _onClick_body: function( e ){ 
        ...
        this._hidePanel();
    }, 

    _hidePanel: function(){
        this._panel.css('display', '');
    }
});

вот только зачем ее автоматически убирать? Пользователь ее и так убрать сможет, кликнув на пустом месте. Главное, чтобы она не перекрывала выделение. А вы еще говорите, что все должно быть максимально просто ;)

рони 06.08.2010 02:45

замена алерта ... как раз висит 3 секунды и удаляется по клику ...
замечания принимаются )))
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
<script language="JavaScript" type="text/javascript">
function position(b) {
    b = typeof b == "string" ? document.getElementById(b) : b;
    var a = document.documentElement,
        c = document.body,
        e = self.pageXOffset || a && a.scrollLeft || c && c.scrollLeft || 0;
        t = self.pageYOffset || a && a.scrollTop || c && c.scrollTop || 0;
    var f = b.offsetHeight,
        g = b.offsetWidth,
        d = document.compatMode == "CSS1Compat" && !window.opera ? a.clientWidth : c.clientWidth;
        a = document.compatMode == "CSS1Compat" && !window.opera ? a.clientHeight : c.clientHeight;
    if (document.compatMode == "CSS1Compat" && window.opera) {
        d = window.innerWidth;
        a = window.innerHeight
    }
    b.style.left = Math.floor(e + (d - g) / 2) + "px";
    b.style.top = Math.floor(t + (a - f) / 2) + "px"
};
window.alert = function (b) {
    b = b.replace(/\n/gim, "<br />");
    var a = document.createElement("div");
    a.style.position = "absolute";
    a.style.color = "#0000FF";
    a.style.backgroundColor = "#FFFACD";
    a.style.border = "1px solid #000";
    a.style.padding = "0px 4px";
    a.innerHTML = b;
    document.onmousedown = function () {
        a && document.body.removeChild(a);
        c && window.clearTimeout(c);
        document.onmousedown = null
    };
    document.body.appendChild(a);
    position(a);
    var c = window.setTimeout(function () {a && document.body.removeChild(a);document.onmousedown = null}, 3E3)
};
</script>
</head>
<body>
<a href="#" onclick="alert('замена алерта'); return 0">кликни</a>
</body>
</html>

Octane 06.08.2010 03:14

Однобуквенные переменные

x-yuri 06.08.2010 06:11

как можно делать замечания, если не хочеться вникать в этот код? (смайлик как у Octane) И при чем тут замена alert'а?

кстати, Octane, ты как-то абстрактно, имхо, подошел к вынесению функциональности. Т.е. я в первую очередь исхожу из того, как что-то будет использоваться. Selection.get() выглядит симпатичнее по сравнению с $Range.stringify($Selection.getRange()). Может, твой вариант чем-то лучше, но я пока не вижу чем. Он сложнее

и все-таки лучше отменять скрытие панели, а то оно может скрыть панель для нового выделения
Function.prototype.makeCancelable = function(){
    var self = this;
    var r = function(){
        if( r.canceled )
            return;
        return self.apply(this, arguments);
    };
    r.cancel = function(){
        this.canceled = true;
    }
    return r;
}

var QuotePanel = new Class({  
    _onMouseUp_body: function( e ){  
        ... 
        this._panel  
            ... 
            .animate( 
                { ... }, 
                { 
                    'complete': function(){ 
                        this._hidePanelCancelable = this._hidePanel().of(this).makeCancelable();
                        setTimeout( this._hidePanelCancelable, 3000 ); 
                    }.of(this) 
                }); 
    }, 
        
    _onClick_body: function( e ){  
        ... 
        if( this._hidePanelCancelable )
            this._hidePanelCancelable.cancel();
        this._hidePanel(); 
    },  
  
    _hidePanel: function(){ 
        this._panel.css('display', ''); 
    } 
});

Octane 06.08.2010 06:47

Цитата:

Сообщение от x-yuri
кстати, Octane, ты как-то абстрактно, имхо, подошел к вынесению функциональности. Т.е. я в первую очередь исхожу из того, как что-то будет использоваться. Selection.get() выглядит симпатичнее по сравнению с $Range.stringify($Selection.getRange()). Может, твой вариант чем-то лучше, но я пока не вижу чем. Он сложнее

Зачем смешивать Selection с Range? Вообще я там поудалял методы, их было больше.

Selection используется редко из-за того, что динамически меняется, как NodeList, а еще в IE почти ничего не умеет, но иногда бывает нужно, поэтому для $Selection оставляем простой интерфейс:
$Selection.get();
$Selection.clear();
$Selection.getRange();
$Selection.selectRange(range);


А большинство работы выполняется с Range:
$Range.create();
$Range.isCollapsed(range);
$Range.stringify(range);
$Range.getRootContainer(range);
$Range.clone(range);
…


Создавать конструктор в этом конкретном случае неудобно, потому что полностью кросс-браузерный интерфейс написать не получится и постоянно обращаться к Range/TextRange-объекту через obj.range бессмысленно, удобнее:
var range = $Selection.getRange(),
    text  = $Range.stringify(range);

if ($Range.standardsCompliant) {
   
    range…

} else {

    range…

}

$Selection.selectRange(range);

рони 06.08.2010 09:12

....
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div id="link">Выделите ниже текст и кликни тут!!!</div>
<div id="link0">всякий разный текст</div>
<div id="link1">всякий разный текст</div>
<div id="link2">всякий разный текст</div>
<script language="JavaScript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script language="JavaScript" type="text/javascript">
$('div').not('#link').mouseup(function () {
var txt=document.selection? document.selection.createRange().text:window.getSelection().toString();
$('#link').one("click",function () {alert(txt)})
});
</script>
</body>

</html>

Cuprum 06.08.2010 09:49

Цитата:

Сообщение от x-yuri
так совсем же просто

Спасибо, буду вникать.
Цитата:

Сообщение от x-yuri
вот только зачем ее автоматически убирать? Пользователь ее и так убрать сможет, кликнув на пустом месте.

Может человек выделил тест не для того чтобы вставить цитату, я для других целей (исключать такую возможность нельзя) - тогда в данном случае панель будет мозолить глаз и надо лишне где-то кликнуть, чтобы ее убрать. Пусть лучше исчезает.
Цитата:

Сообщение от x-yuri
Главное, чтобы она не перекрывала выделение

А что не так с перекрытием? Если фрагмент выделен большой, то панель в любом случае будет перекрывать выделение (всплывать над ним). Правда если внешний клик после этого будет на области выделения, то выходит чудно - в Fx и Chrome выделение снимается после события click, а в Opera - уже после события mousedown.
Цитата:

Сообщение от x-yuri
А вы еще говорите, что все должно быть максимально просто

Простота при тщательном изучении всегда усложняется :yes:

Приведенные примеры посмотрю попозже, убегаю сейчас.

x-yuri 06.08.2010 20:53

Цитата:

Сообщение от Cuprum
Может человек выделил тест не для того чтобы вставить цитату, я для других целей (исключать такую возможность нельзя) - тогда в данном случае панель будет мозолить глаз и надо лишне где-то кликнуть, чтобы ее убрать. Пусть лучше исчезает.

а если человек отвлекся/задумался, а оно уже исчезло. Опять выделять?

Octane, да, я c Range, Selection не особо много работал. Просто подумал, что $Selection.get могло бы сразу текст возвращать. Или как вариант можно было бы метод getText написать. Но в общем-то мне сложно судить...

рони, хватит one-liner'ами досить ;)

Octane 06.08.2010 23:37

Цитата:

Сообщение от x-yuri
Просто подумал, что $Selection.get могло бы сразу текст возвращать.

Насколько я знаю, из document.selection не вытащить выделенный текст, надо сначала TextRange создать. В остальных браузерах Selection хоть и преобразуется в строку, но по спецификации выделение может содержать несколько Range-объектов, поэтому:
var range = $Selection.getRange(), // получаем TextRange или *!*первый*/!* Range
    text  = $Range.stringify(range); // получаем выделенный текст

Хотя ничего страшного, наверное, не будет, если $Selection добавить метод getText:
$Selection = {
   …
   getText: function () {
        return $Range.stringify(this.getRange());
   }
};
Кому как нравится :) Просто не хотел, чтобы $Selection знал, как работать с Range/TextRange, но этого избежать не совсем получилось, метод для восстановления выделения, все же знает, что надо выполнить textrange.select();

Cuprum 10.08.2010 00:09

<!DOCTYPE html>
<html><head>
	<title>Работа с выделениями</title>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8">
	<style type="text/css">
		#past {height:30px; padding:5px; border:1px solid #000; text-align:center; line-height:30px; color:#000; font:12px Arial, sans-serif; background:#fff; cursor:pointer;}
	</style>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
	<script type="text/javascript">
		 $(document).ready(function() {
	var qPanel = $('<div id="past" style="position:absolute; display:none;"><a href="" id="link">Вставить цитату в поле<a></div>');
	var textarea = $('textarea'); 
	var text;

$Selection = {
	standardsCompliant: typeof getSelection != "undefined",
	get: function () {
		return this.standardsCompliant ? getSelection() : document.selection;
	},
	getRange: function () {
		return this.get()[this.standardsCompliant ? "getRangeAt" : "createRange"](0);
	}
};

$Range = {
	standardsCompliant: $Selection.standardsCompliant,
	stringify: function (range) {
		return this.standardsCompliant ? range.toString() : range.text;
	}
};

	qPanel.click(function () {
		textarea.val(textarea.val() + '<blockquote>' + text + '</blockquote>\r\n').focus();
		$(this).hide();
		return false;
	});

	qPanel.appendTo('body');

	$('#paragraph').mouseup(function(e){
			text = $Range.stringify($Selection.getRange());
			var widthqPanel = qPanel.outerWidth();
			if (text)
			{
				qPanel
				.css({
					top: e.pageY - 40,
					left: e.pageX - widthqPanel/3,
					display: 'block',
					opacity: 0
				})
				.animate({
					top: '-=' + 10 + 'px',
					opacity: 1
				}, 250);
			}
			e.stopPropagation();
	});

	$('body').click(function() {
		if (!text) qPanel.hide();
	});

});
	</script>

</head><body>
<p id="paragraph">Proin gravida auctor velit vitae facilisis. Vestibulum ac lacus vitae nunc vulputate sodales a eget augue. Quisque ornare enim a nibh ullamcorper volutpat. Nulla imperdiet gravida pulvinar. Proin consequat nisi sit amet augue convallis nec viverra nisl mollis. Nulla in orci metus? Morbi interdum ligula vitae tortor elementum sodales. Curabitur ut ante vitae lectus egestas commodo congue non tortor. Cras mi massa, vulputate id rhoncus ac, facilisis sed leo! Mauris purus urna; ultrices non sodales sed, molestie in neque. Vestibulum ullamcorper orci ac dolor commodo ac aliquam nunc ultrices. Aliquam dapibus congue massa, eleifend vestibulum ligula viverra vitae.</p>

<form action="" name="">
	<textarea cols="50" rows="10"></textarea>
</form>

</body></html>

Итак, воспользовался способом Octane (в данном случае он мне больше подходит), отказался от setTimeout (согласен с x-yuri) Возникли вопросы:
1. Не совсем понятно почему панель не скрывается после выделения если сделать клик не на параграфе на котором произошло выделение. (по логике в <body> входит не только #paragraph, но и, например, textarea)
2. Если выделение текста велико, внешний клик (чтобы скрыть панель) вполне может быть на самой области выделения. Однако панель всплывает повторно. Только Opera ведет себя адекватно (а может и неадекватно, как посмотреть) скрывая панель по клику на самом выделении.
Прошу разъяснить :help:

Octane 10.08.2010 01:38

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Работа с выделениями</title>
	<style type="text/css">
		body {
			color: #000;
			position: relative;
		}
		.paragraph {
			border: 1px solid #000;
		}
		.quote-popup-menu {
			position: absolute;
			padding: 10px;
			background: #ffc;
			border: 1px solid #000;
		}
			.quote-popup-menu .btn:hover {
				color: #fff;
				background: #33c;
				cursor: pointer;
			}
		.hidden {
			display: none;
		}
	</style>
</head>
<body>
	<div>
		<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p>
		<p class="paragraph">Proin gravida auctor velit vitae facilisis. Vestibulum ac lacus vitae nunc vulputate sodales a eget augue. Quisque ornare enim a nibh ullamcorper volutpat. Nulla imperdiet gravida pulvinar. Proin consequat nisi sit amet augue convallis nec viverra nisl mollis. Nulla in orci metus? Morbi interdum ligula vitae tortor elementum sodales. Curabitur ut ante vitae lectus egestas commodo congue non tortor. Cras mi massa, vulputate id rhoncus ac, facilisis sed leo! Mauris purus urna; ultrices non sodales sed, molestie in neque. Vestibulum ullamcorper orci ac dolor commodo ac aliquam nunc ultrices. Aliquam dapibus congue massa, eleifend vestibulum ligula viverra vitae.</p>
		<p>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.</p>
		<form action="">
			<fieldset>
				<textarea id="editor-text" name="editor-text" cols="50" rows="10"></textarea>
			</fieldset>
		</form>
	</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">

jQuery.bindContext = function (func, thisObj) {
	return function (event) {
		return func.call(thisObj, event);
	};
};

var $Selection = {
	standardsCompliant: typeof getSelection != "undefined",
	get: function () {
		return this.standardsCompliant ? getSelection() : document.selection;
	},
	getRange: function () {
		return this.get()[this.standardsCompliant ? "getRangeAt" : "createRange"](0);
	}
};

var $Range = {
	standardsCompliant: $Selection.standardsCompliant,
	stringify: function (range) {
		return this.standardsCompliant ? range.toString() : range.text;
	},
	isCollapsed: function (range) {
		return this.standardsCompliant ? range.collapsed : !range.htmlText.length;
	},
	getRootContainer: function (range) {
		if (this.standardsCompliant) {
			var root = range.commonAncestorContainer;
			return root.nodeType == this.TEXT_NODE ? root.parentNode : root;
		}
		return range.parentElement();
	}
};

var quote = function ($) {
	return {
		template: [
			'<blockquote>',
				'<p>',
					'{QUOTE}',
				'</p>',
			'</blockquote>',
			'\r\n'
		].join(""),

		templateRegExp: /{QUOTE}/,

		menuTemplate: [
			'<div class="quote-popup-menu hidden">',
				'<span class="btn">Вставить цитату</span>',
			'</div>'
		].join(""),

		root: "body",
		text: ".paragraph",
		menu: ".quote-popup-menu",
		btn: ".btn",
		textarea: "#editor-text",

		done: false,
		range: null,

		$menu: null,
		$btn: null,
		$textarea: null,

		init: function (cfg) {
			if (this.done) {
				return;
			}
			this.done = true;

			$.extend(this, cfg)

			this.$textarea = $(this.textarea);

			this.createMenu();
			this.initEvents();
		},
		initEvents: function () {
			$(document).click($.bindContext(this.toggleMenu, this));
			this.$btn.click($.bindContext(this.paste, this));
		},
		createMenu: function () {
			this.$menu = $(this.menuTemplate);
			this.$btn = this.$menu.find(this.btn);
			this.$menu.appendTo(this.root);
		},
		paste: function (event) {
			this.$textarea.val(this.$textarea.val() + this.template.replace(this.templateRegExp, $Range.stringify(this.range)));
		},
		saveSelection: function () {
			this.range = $Selection.getRange();
		},
		needShowMenu: function () {
			this.saveSelection();
			var $root = $($Range.getRootContainer(this.range));
			return this.$menu.is(":hidden") && !$Range.isCollapsed(this.range) && ($root.is(this.text) || $root.parent(this.text).length);
		},
		toggleMenu: function (event) {
			if (this.needShowMenu()) {
				this.showMenu(event.pageX, event.pageY)
			} else {
				this.hideMenu();
			}
		},
		showMenu: function (x, y) {
			this.$menu.css({
				top:  y + "px",
				left: x + "px"
			}).show();
		},
		hideMenu: function () {
			this.$menu.hide();
		}
	};
}(jQuery);

$(function () {

	quote.init();

});
</script>
</body>
</html>

Cuprum 10.08.2010 14:46

Octane, Спасибо бо!

Octane 10.08.2010 16:19

В Opera не генерируется событие click при выделении текста.
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Работа с выделениями</title>
	<style type="text/css">
		body {
			color: #000;
			position: relative;
		}
		.paragraph {
			border: 1px solid #000;
		}
		.quote-popup-menu {
			position: absolute;
			padding: 10px;
			background: #ffc;
			border: 1px solid #000;
		}
			.quote-popup-menu .btn:hover {
				color: #fff;
				background: #33c;
				cursor: pointer;
			}
		.hidden {
			display: none;
		}
	</style>
</head>
<body>
	<div>
		<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p>
		<p class="paragraph">Proin gravida auctor velit vitae facilisis. Vestibulum ac lacus vitae nunc vulputate sodales a eget augue. Quisque ornare enim a nibh ullamcorper volutpat. Nulla imperdiet gravida pulvinar. Proin consequat nisi sit amet augue convallis nec viverra nisl mollis. Nulla in orci metus? Morbi interdum ligula vitae tortor elementum sodales. Curabitur ut ante vitae lectus egestas commodo congue non tortor. Cras mi massa, vulputate id rhoncus ac, facilisis sed leo! Mauris purus urna; ultrices non sodales sed, molestie in neque. Vestibulum ullamcorper orci ac dolor commodo ac aliquam nunc ultrices. Aliquam dapibus congue massa, eleifend vestibulum ligula viverra vitae.</p>
		<p>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.</p>
		<form action="">
			<fieldset>
				<textarea id="editor-text" name="editor-text" cols="50" rows="10"></textarea>
			</fieldset>
		</form>
	</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
var $Selection = {
	standardsCompliant: typeof getSelection != "undefined",
	get: function () {
		return this.standardsCompliant ? getSelection() : document.selection;
	},
	getRange: function () {
		return this.get()[this.standardsCompliant ? "getRangeAt" : "createRange"](0);
	}
};

var $Range = {
	standardsCompliant: $Selection.standardsCompliant,
	stringify: function (range) {
		return this.standardsCompliant ? range.toString() : range.text;
	},
	isCollapsed: function (range) {
		return this.standardsCompliant ? range.collapsed : !range.htmlText.length;
	},
	getRootContainer: function (range) {
		if (this.standardsCompliant) {
			var root = range.commonAncestorContainer;
			return root.nodeType == this.TEXT_NODE ? root.parentNode : root;
		}
		return range.parentElement();
	}
};

var quote = function ($) {
	return {
		template: [
			'<blockquote>',
				'<p>',
					'{QUOTE}',
				'</p>',
			'</blockquote>',
			'\r\n'
		].join(""),

		templateRegExp: /{QUOTE}/,

		menuTemplate: [
			'<div class="quote-popup-menu hidden">',
				'<span class="btn">Вставить цитату</span>',
			'</div>'
		].join(""),

		root: "body",
		text: ".paragraph",
		btn: ".btn",
		textarea: "#editor-text",

		done: false,
		range: null,

		$menu: null,
		$btn: null,
		$textarea: null,

		init: function (cfg) {
			if (this.done) {
				return;
			}
			this.done = true;

			$.extend(this, cfg)

			this.$textarea = $(this.textarea);

			this.createMenu();
			this.initEvents();
		},
		initEvents: function () {
			var quote = this;
			$(document).mousedown(function () {
				quote.hideMenu();
			}).mouseup(function (event) {
				quote.showMenu(event);
			});
			this.$btn.bind("click mousedown mouseup", function (event) {
				if (event.type == "click") {
					quote.paste();
				}
				event.stopPropagation();
			});
		},
		createMenu: function () {
			this.$menu = $(this.menuTemplate);
			this.$btn = this.$menu.find(this.btn);
			this.$menu.appendTo(this.root);
		},
		paste: function (event) {
			this.hideMenu();
			this.$textarea.val(this.$textarea.val() + this.template.replace(this.templateRegExp, $Range.stringify(this.range)));
		},
		saveSelection: function () {
			this.range = $Selection.getRange();
		},
		needShowMenu: function () {
			this.saveSelection();
			var $root = $($Range.getRootContainer(this.range));
			return !$Range.isCollapsed(this.range) && ($root.is(this.text) || $root.parent(this.text).length);
		},
		showMenu: function (event) {
			if (!this.needShowMenu()) {
				return;
			}
			this.$menu.css({
				top:  event.pageY + "px",
				left: event.pageX + "px"
			}).show();
		},
		hideMenu: function () {
			this.$menu.hide();
		}
	};
}(jQuery);

$(function () {

	quote.init();

});
</script>
</body>
</html>


Часовой пояс GMT +3, время: 16:45.