var textEl = document.getElementById("testText");
textEl.style.display = "none";
try{
var a = textEl.selectionStart;
}catch(e){
alert(e);
}
textEl是一个很简单的html的input输入框。但是在设置隐藏之后获取选中的起始和结束位置就会报异常。
异常如下:
"[Exception...
"Component returned failure code: 0x80004005 (NS_ERROR_FAILURE)
[nsIDOMHTMLInputElement.selectionStart]" nsresult: "0x80004005
(NS_ERROR_FAILURE)" location: "JS frame ::
file:///C:/1.html
:: <TOP_LEVEL> :: line 15" data: no]"
很明显这是从firefox内核中报出来的异常。
我们来看下获取input选中起始位置的源码,在content\html\content\src\nsHTMLInputElement.cpp文件中:
nsresult
nsHTMLInputElement::GetSelectionRange(PRInt32* aSelectionStart,
PRInt32* aSelectionEnd)
{
nsresult rv = NS_ERROR_FAILURE;
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
if (textControlFrame)
rv = textControlFrame->GetSelectionRange(aSelectionStart, aSelectionEnd);
}
return rv;
}
重要的是nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);这句代码,
在input隐藏的时候,它返回了null。为什么呢?因为传进去的参数是PR_TRUE!!!
ok,我们跟进去看为什么会返回null,经过中间几个小方法的调用,我们看content\html\content\src\nsGenericHTMLElement.cpp中的方法:
// static
nsIFormControlFrame*
nsGenericHTMLElement::GetFormControlFrameFor(nsIContent* aContent,
nsIDocument* aDocument,
PRBool aFlushContent)
{
if (aFlushContent) {
// Cause a flush of the frames, so we get up-to-date frame information
aDocument->FlushPendingNotifications(Flush_Frames);
}
nsIFrame* frame = GetPrimaryFrameFor(aContent, aDocument);
if (frame) {
nsIFormControlFrame* form_frame = do_QueryFrame(frame);
if (form_frame) {
return form_frame;
}
// If we have generated content, the primary frame will be a
// wrapper frame.. out real frame will be in its child list.
for (frame = frame->GetFirstChild(nsnull);
frame;
frame = frame->GetNextSibling()) {
form_frame = do_QueryFrame(frame);
if (form_frame) {
return form_frame;
}
}
}
return nsnull;
}
这个方法里面用到了我们的PR_TRUE参数,是会调用aDocument->FlushPendingNotifications(Flush_Frames);
我们继续跟进,我们会来到layout\base\nsPresShell.cpp中的FlushPendingNotifications方法,在这个方法中,firefox会处理本shell(本iframe)中以前挂起的一些操作(比如说我们设置display为none),其中有一段代码就是处理挂起的样式操作,如下所示:
// Process pending restyles, since any flush of the presshell wants
// up-to-date style data.
if (!mIsDestroying) {
mPresContext->FlushPendingMediaFeatureValuesChanged();
// Flush any pending update of the user font set, since that could
// cause style changes (for updating ex/ch units, and to cause a
// reflow).
mPresContext->FlushUserFontSet();
nsAutoScriptBlocker scriptBlocker;
mFrameConstructor->ProcessPendingRestyles();
}
在处理pending restyles的时候会进入到mFrameConstructor->ProcessPendingRestyles()中,它会处理一个
mPendingRestyles列表中被添加的所有的pending restyles。 我们设置的display为none会产生一个hint为
nsChangeHint_ReconstructFrame的pending restyles。
firefox会根据这个hint重现构建这个frame(不可见的容器会从nsFrameManager中移除),就是调用的
RecreateFramesForContent这个方法,这个方法里调用ContentRemoved方法,ContentRemoved方法
::DeletingFrameSubtree(frameManager, childFrame)来从nsFrameManager中删除掉这个不可见的元素,所以
在FlushPendingNotifications之后我们就无法取到这个formControlFrame了,所以firefox内核返回了
NS_ERROR_FAILURE,导致了js抛出异常。
附1: 设置display为none是怎么加到pending restyles中的
看下调用堆栈
void
nsCSSFrameConstructor::PostRestyleEvent(nsIContent* aContent,
nsReStyleHint aRestyleHint,
nsChangeHint aMinChangeHint)
{
if (NS_UNLIKELY(mPresShell->IsDestroying())) {
return;
}
if (aRestyleHint == 0 && !aMinChangeHint) {
// Nothing to do here
return;
}
NS_ASSERTION(aContent->IsNodeOfType(nsINode::eELEMENT),
"Shouldn't be trying to restyle non-elements directly");
RestyleData existingData;
existingData.mRestyleHint = nsReStyleHint(0);
existingData.mChangeHint = NS_STYLE_HINT_NONE;
mPendingRestyles.Get(aContent, &existingData);
existingData.mRestyleHint =
nsReStyleHint(existingData.mRestyleHint | aRestyleHint);
NS_UpdateHint(existingData.mChangeHint, aMinChangeHint);
mPendingRestyles.Put(aContent, existingData);
PostRestyleEventInternal();
}
ok,就是在这个方法里面加入进去的。
附2:为什么得到input的value是没问题的
input设置隐藏之后,即使从nsFrameManager移除掉对应的frame,也可以得到正确的value的值,这是为什么,
我们看下content\html\content\src\nsHTMLInputElement.cpp中对应的方法:
NS_IMETHODIMP
nsHTMLInputElement::GetValue(nsAString& aValue)
{
if (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD) {
// No need to flush here, if there's no frame created for this
// input yet, there won't be a value in it (that we don't already
// have) even if we force it to be created
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
PRBool frameOwnsValue = PR_FALSE;
if (formControlFrame) {
nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
if (textControlFrame) {
textControlFrame->OwnsValue(&frameOwnsValue);
} else {
// We assume if it's not a text control frame that it owns the value
frameOwnsValue = PR_TRUE;
}
}
if (frameOwnsValue) {
formControlFrame->GetFormProperty(nsGkAtoms::value, aValue);
} else {
if (!GET_BOOLBIT(mBitField, BF_VALUE_CHANGED) || !mValue) {
GetDefaultValue(aValue);
} else {
CopyUTF8toUTF16(mValue, aValue);
}
}
return NS_OK;
}
if (mType == NS_FORM_INPUT_FILE) {
if (nsContentUtils::IsCallerTrustedForCapability("UniversalFileRead")) {
if (!mFileNames.IsEmpty()) {
aValue = mFileNames[0];
}
else {
aValue.Truncate();
}
} else {
// Just return the leaf name
nsCOMArray<nsIFile> files;
GetFileArray(files);
if (files.Count() == 0 || NS_FAILED(files[0]->GetLeafName(aValue))) {
aValue.Truncate();
}
}
return NS_OK;
}
// Treat value == defaultValue for other input elements
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue) &&
(mType == NS_FORM_INPUT_RADIO || mType == NS_FORM_INPUT_CHECKBOX)) {
// The default value of a radio or checkbox input is "on".
aValue.AssignLiteral("on");
}
if (mType != NS_FORM_INPUT_HIDDEN) {
aValue = nsContentUtils::TrimCharsInSet(kWhitespace, aValue);
}
return NS_OK;
}
GetValue方法也会首先去获取frame,但是它传进去的值是PR_FALSE,也就是说如果在设置隐藏后不首先调用
textEl.selectionStart之类的方法的话,此处得到的是正确的frame,那也就是可以得到正确的值。但是如果我们首先调用了textEl.selectionStart的话,此input对应的frame已经被移除,所以得到的frame将是空的,会得到错误的值么?不会!
其实nsHTMLInputElement使用了一种模型+控件的模式,你在设置值的时候,如果可以得到对应的frame(控件),则值被设置到控件上,如果得不到frame(input设置了隐藏等),则firefox会首先把值保存在模型上,而firefox会保持一些监听器,等控件重现展现的时候把模型的值同步到控件上。而你取值的时候也会保持同样的规则。
附3: 哪些方法在input隐藏后调用会报错
在nsHTMLInputElement使用到GetFormControlFrame(PR_TRUE)的有下面6个方法:
void
nsHTMLInputElement::SelectAll
NS_IMETHODIMP
nsHTMLInputElement::SetSelectionRange
NS_IMETHODIMP
nsHTMLInputElement::SetSelectionStart
NS_IMETHODIMP
nsHTMLInputElement::SetSelectionEnd
nsresult
nsHTMLInputElement::GetSelectionRange
NS_IMETHODIMP
nsHTMLInputElement::GetPhonetic
但是由于SelectAll没有返回值,GetPhonetic即使frame为null也会返回NS_OK。所以在input框隐藏的时候,用js来设置和获取选中的起始和结束位置都会在js里抛出异常,而其他方法都是安全的。
估计Mozilla认为在文本框隐藏的时候,选择位置都是没有意义的,不应该在文本框隐藏的时候去涉及选择位置。
分享到:
相关推荐
//FireFox、Opera等浏览器支持的创建方式 } else { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");//IE浏览器支持的创建方式 } } //直接通过XMLHttpRequest对象获取远程网页源代码 function getSource()...
火狐浏览器插件是一种增强火狐功能的软件组件,它们通常是用JavaScript、XUL或CSS等Web技术编写的,能够扩展火狐的功能,提供个性化的浏览体验。在本例中,我们关注的是一个特定的插件,其核心功能是获取计算机的MAC...
在Firefox中,获取电脑名和MAC地址更为复杂,因为浏览器的安全策略更加严格。火狐并不直接支持ActiveXObject,但可以使用一些插件或者WebExtensions来扩展其能力。由于获取MAC地址涉及用户隐私,大多数现代浏览器都...
- **`event.x`与`event.y`**:IE中直接提供`event.x`和`event.y`属性来获取鼠标位置,而在Firefox中则需使用`event.pageX`和`event.pageY`。此外,`event.clientX`和`event.clientY`在两个浏览器中的表现也有所不同...
标题与描述均聚焦于“JavaScript在IE和Firefox(火狐)的不兼容问题解决”,这表明文章旨在探讨并提供解决方案来处理在不同浏览器环境下的JavaScript兼容性问题,尤其是在Internet Explorer(IE)和Mozilla Firefox...
在这个项目中,我们从GitHub上获取了Firefox iOS的源代码,版本为Firefox Beta v6.0b4。 首先,我们要理解Firefox for iOS的架构。与桌面版Firefox不同,iOS版基于Apple的WebKit框架构建,因为Apple不允许在iOS上...
另外还支持Firefox火狐浏览器URL的获取。支持效果极佳。 如果您还需要支持其它浏览器,请联系加我的QQ群:40308283 说明一点,软件是用delphi 7 开发的。绿色软件。 最新版本下载地址:...
"JS日期选择器(兼容IE,FireFox,Oprea,chrome等主流浏览器)"是一个专门为JavaScript环境设计的,旨在解决跨浏览器兼容性问题的日期选择工具。这个组件的目标是确保在不同浏览器如Internet Explorer(IE)、Firefox、...
《Firefox火狐浏览器51.0-win32版本:深入解析与使用指南》 Firefox火狐浏览器,由Mozilla基金会开发,是一款全球广受欢迎的开源网络浏览器。本文将围绕"Firefox火狐浏览器官方51.0-win32版本exe安装包"进行详细...
在Firefox中,Venkman是一个强大的JavaScript调试器,专为深入理解JavaScript代码的行为而设计。以下是在Firefox中使用Venkman进行JavaScript调试的详细步骤。 ### 下载和安装Venkman 1. **方法1**:你可以直接...
标题“火狐兼容获取鼠标的坐标”涉及到的是在网页开发中如何在Firefox浏览器上获取鼠标指针的位置。在Web开发中,特别是在JavaScript编程中,获取鼠标坐标是一项常见的需求,用于实现各种交互效果,如拖放功能、点击...
Deepin Linux 安装最新版 Firefox火狐浏览器详解 在 Deepin Linux 操作系统中,安装最新版的 Firefox 火狐浏览器可以通过下载最新版的 Firefox 压缩包并解压缩来实现。下面是详细的安装步骤: 首先,用户需要在 ...
火狐Firefox是一款深受全球用户喜爱的开源网络浏览器,它的24版本在当时被视为一个重要的里程碑。这个版本的Firefox以其稳定性、安全性和性能优化为特点,是开发者和测试人员的理想选择。下面将详细介绍Firefox 24...
【Firefox Home源码】是Firefox浏览器为iOS平台开发的一款辅助应用,它的主要功能是帮助用户在iPhone设备上无缝同步和访问他们在桌面电脑上的Firefox浏览数据,包括历史记录、书签和打开的标签页。这款应用并非一个...
火狐浏览器(Mozilla Firefox)是一款由Mozilla基金会和志愿者共同开发的开源网络浏览器,以其高度定制性、安全性和性能而受到用户欢迎。以下是一份描述: 火狐浏览器是一款跨平台的开源网络浏览器,由Mozilla基金...
经过三天的苦战,查询无数的国外网站终于实现了对IE和...利用API和DDE分别对IE和FireFox进行了浏览器地址获取,完整的源码程序,与大家分享学习。后期将完善,实现对IE,FireFox,360,搜狗等主流浏览器URL地址的监控。
【Firefox插件例子源码】是一个关于Firefox浏览器插件开发的学习资源,包含了示例代码,可供开发者参考和学习。这个压缩包中包含了多个文件,它们各自在插件开发过程中扮演着不同的角色。以下是对这些文件及其相关的...
《Firefox火狐浏览器1.5.0.6-win32版本安装详解》 Firefox火狐浏览器,由Mozilla基金会开发,是一款开源、免费的网络浏览器,以其强大的安全性、隐私保护和高度可定制性著称。本资源是Firefox 1.5.0.6-win32的官方...
标题中的“firefox debug 调js有用”表明我们主要探讨的是使用Firefox浏览器的调试工具来对JavaScript代码进行调试。在Web开发中,JavaScript是至关重要的客户端脚本语言,用于实现网页的动态交互功能。Firefox提供...
### Javascript的IE与Firefox(火狐)兼容性解决方案 在Web开发过程中,浏览器兼容性问题一直是开发者们关注的重点之一。由于不同的浏览器对于Web标准的支持程度存在差异,这导致了同样的代码在不同浏览器中的表现...